Introduction
During the last 30 years, modern epidemiology has been able to identify significant limitations of classic epidemiologic methods when the focus is to explain the main effect of a risk factor on a disease or outcome.
Causal Inference based on the Neyma-Rubin Potential Outcomes Framework (Rubin, 2011), first introduced in Social Science by Donal Rubin (Rubin, 1974) and later in Epidemiology and Biostatistics by James Robins (Greenland and Robins, 1986), has provided the theory and statistical methods needed to overcome recurrent problems in observational epidemiologic research, such as:
- non-collapsibility of the odds and hazard ratios,
- impact of paradoxical effects due to conditioning on colliders,
- selection bias related to the vague understanding of the effect of time on exposure and outcome and,
- effect of time-dependent confounding and mediators,
- etc.
Causal effects are often formulated regarding comparisons of potential outcomes, as formalised by Rubin (Rubin, 2011). Let A denote a binary exposure, W a vector of potential confounders, and Y a binary outcome. Given A, each individual has a pair of potential outcomes: the outcome when exposed, denoted \(Y_{1}\), and the outcome when unexposed, \(Y_{0}\). These quantities are referred to as potential outcomes since they are hypothetical, given that it is only possible to observe a single realisation of the outcome for an individual; we observe \(Y_{1}\) only for those in the exposure group and \(Y_{0}\) only for those in the unexposed group (Rubin, 1974). A common causal estimand is the Average Treatment Effect (ATE), defined as \(E[Y_{1}\, – \,Y_{0}]\).
Classical epidemiologic methods use regression adjustment to explain the main effect of a risk factor measure on a disease or outcome. Regression adjustment control for confounding but requires making the assumption that the effect measure is constant across levels of confounders included in the model. However, in non-randomized observational studies, the effect measure is not constant across groups given the different distribution of individual characteristics at baseline.
James Robins in 1986 demonstrated that using the G-formula a generalization of the standardisation, allows obtaining a unconfounded marginal estimation of the ATE under causal untestable assumptions, namely conditional mean independence, positivity and consistency or stable unit treatment value assignment (SUTVA) (Greenland and Robins, 1986), (Robins et al., 2000):
The G-Formula and ATE estimation
\[\psi(P_{0})\,=\,\sum_{w}\,\left[\sum_{y}\,P(Y=y\mid A=1,W=w)-\,\sum_{y}\,P(Y = y\mid A=0,W=w)\right]P(W=w)\]
where,
\[P(Y = y \mid A = a, W = w)\,=\,\frac{P(W = w, A = a, Y = y)}{\sum_{y}\,P(W = w, A = a, Y = y)}\]
is the conditional probability distribution of Y = y, given A = a, W = w and,
\[P(W = w)\,=\,\sum_{y,a}\,P(W = w, A = a, Y = y)\]
The ATE can be estimated non-parametrically using the G-formula. However, the course of dimensionality in observational studies limits its estimation. Hence, the estimation of the ATE using the G-formula relies mostly on parametric modelling and maximum likelihood estimation.
The correct model specification in parametric modelling is crucial to obtain unbiased estimates of the true ATE (Rubin, 2011). Alternatively, propensity score methods, introduced by Rosenbaum and Rubin (Rosenbaum and Rubin, 1983), are also commonly used for estimation of the ATE. The propensity score is a balancing score that can be used to create statistically equivalent exposure groups to estimate the ATE via matching, weighting, or stratification (Rosenbaum and Rubin, 1983).
However, very low or very high propensity scores can lead to very large weights, resulting in unstable ATE estimates with high variance and values outside the constraints of the statistical model (Lunceford and Davidian, 2004).
Furthermore, when analyizing observational data with a large number of variables and potentially complex relationships among them, model misspecification during estimation is of particular concern. Hence, the correct model specification in parametric modelling is crucial to obtain unbiased estimates of the true ATE (Laan and Rose, 2011).
However, Mark van der Laan and Rubin (Laan and Rubin, 2006) introduced in 2006 a double-robust estimation procedure to reduce bias against misspecification. The targeted maximum likelihood estimation (TMLE) is a semiparametric, efficient substitution estimator (Laan and Rose, 2011).
TMLE
TMLE allows for data-adaptive estimation while obtaining valid statistical inference based on the targeted minimum loss-based estimation and machine learning algorithms to minimise the risk of model misspecification (Laan and Rose, 2011). The main characteristics of TMLE are:
TMLE is a general algorithm for the construction of double-robust, semiparametric, efficient substitution estimators. TMLE allows for data-adaptive estimation while obtaining valid statistical inference.
TMLE implementation uses the G-computation estimand (G-formula). Briefly, the TMLE algorithm uses information in the estimated exposure mechanism P(A|W) to update the initial estimator of the conditional expectation of the outcome given the treatment and the set of covariates W, E\(_{0}\)(Y|A,W).
The targeted estimates are then substituted into the parameter mapping \(\Psi\). The updating step achieves a targeted bias reduction for the parameter of interest \(\Psi(P_{0})\) (the true target parameter) and serves to solve the efficient score equation, namely the Influence Curve (IC). As a result, TMLE is a double-robust estimator.
TMLE it will be consistent for \(\Psi(P_{0})\) if either the conditional expectation E\(_{0}\)(Y|A,W) or the exposure mechanism P\(_{0}\)(A|W) are estimated consistently.
TMLE will be efficient if the previous two functions are consistently estimated achieving the lowest asymptotic variance among a large class of estimators. These asymptotic properties typically translate into lower bias and variance in finite samples (Bühlmann et al., 2016).
The general formula to estimate the ATE using the TMLE method:
\[\psi TMLE,n = \Psi(Q_{n}^{*})= {\frac{1}{n}\sum_{i=1}^{n}\bar{Q}_{n}^{1}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{1}\left(0,\ W_{i}\right)}. (1)\] 7. The efficient influcence curve (IC) based on Hampel seminal paper (Hampel, 1974) is applied for statistical inference using TMLE:
\[IC_{n}(O_{i})=\left(\frac{I\left(A_{i}=1\right)}{g_n\left(1\left|W_{i}\right)\right)}\ -\ \frac{I\left(A_{i}=0\right)}{g_n\left(0\left|W_{i}\right)\right)}\ \right)\left[Y_{i}-\bar{Q}_{n}^{1}\left(A_{i},W_{i}\right)\right]+\bar{Q}_{n}^{1}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{1}\left(0,\ W_{i}\right) - \psi TMLE,n. (2)\]
where the variance of the ATE:
\[\sigma({\psi_{0}})=\sqrt{\frac{Var(IC_{n})}{n}}. (3)\]
- The procedure is available with standard software such as the tmle package in R (Gruber and Laan, 2011).
Structural causal framework
Direct Acyclic Graph (DAG)
Figure 1. Direct Acyclic Graph
Source: Miguel Angel Luque-Fernandez
DAG interpretation
The ATE is interpreted as the population risk difference in one-year mortality for lung cancer patients diagnosed via emergency presentations versus non-emergency presentations. Under causal assumptions, and compared with non-emergency presentations of lung cancer, the risk difference of one-year mortality for emergency presentations increases by approximately 20%.
Causal assumptions
To estimate the value of the true causal target parameter \(\psi(P_{0})\) with a model for the true data generation process \(P_{0}\) under the counterfactual framework augmented additional untestable cuasal assumptions have to be considered (Rubin, 2011), (Laan and Rose, 2011):
CMI or Randomization
(\(Y_{0},Y_{1}\perp\)A|W) or conditional mean independence (CMI) of the binary treatment effect (A) on the outcome (Y) given the set of observed covariates (W), where W = (W1, W2, W3, … , \(\text{W}_{k}\)).
Positivity
a ϵ A: P(A=a | W) > 0
P(A=1|W=w) > 0 and P(A=0| W = w) > 0 for each possible w.
Consistency or SUTVA
The Stable Unit Treatment Value Assumption (SUTVA) incorporates both this idea that units do not interfere with one another, and also the concept that for each unit there is only a single version of each treatment level.
TMLE flow chart
Figure 2. TMLE flow chart (Road map)
Adapted from: Mark van der Laan and Sherri Rose. Targeted learning: causal inference for observational and experimental dataSpringer Series in Statistics, 2011.
Data generation
Simulation
In R we create a function to generate the data. The function will have as input number of draws and as output the generated observed data (ObsData) including the counterfactuals (Y1, Y0).
The simulated data replicationg the DAG in Figure 1:
- Y: mortality binary indicator (1 death, 0 alive)
- A: binary treatment for emergency presentation at cancer diagnosis (1 EP, 0 NonEP)
- W1: Gender (1 male; 0 female)
- W2: Age at diagnosis (0 <65; 1 >=65)
- W3: Cancer TNM classification (scale from 1 to 4)
- W4: Comorbidities (scale from 1 to 5)
options(digits=4)
generateData <- function(n){
w1 <- rbinom(n, size=1, prob=0.5)
w2 <- rbinom(n, size=1, prob=0.65)
w3 <- round(runif(n, min=0, max=4), digits=3)
w4 <- round(runif(n, min=0, max=5), digits=3)
A <- rbinom(n, size=1, prob= plogis(-0.4 + 0.2*w2 + 0.15*w3 + 0.2*w4 + 0.15*w2*w4))
Y <- rbinom(n, size=1, prob= plogis(-1 + A -0.1*w1 + 0.3*w2 + 0.25*w3 + 0.2*w4 + 0.15*w2*w4))
# counterfactual
Y.1 <- rbinom(n, size=1, prob= plogis(-1 + 1 -0.1*w1 + 0.3*w2 + 0.25*w3 + 0.2*w4 + 0.15*w2*w4))
Y.0 <- rbinom(n, size=1, prob= plogis(-1 + 0 -0.1*w1 + 0.3*w2 + 0.25*w3 + 0.2*w4 + 0.15*w2*w4))
# return data.frame
data.frame(w1, w2, w3, w4, A, Y, Y.1, Y.0)
}
set.seed(7777)
ObsData <- generateData(n=10000)
True_Psi <- mean(ObsData$Y.1-ObsData$Y.0);
cat(" True_Psi:", True_Psi)
True_Psi: 0.198
Bias_Psi <- lm(data=ObsData, Y~ A + w1 + w2 + w3 + w4)
cat("\n")
cat("\n Naive_Biased_Psi:",summary(Bias_Psi)$coef[2, 1])
Naive_Biased_Psi: 0.2073
Naive_Bias <- ((summary(Bias_Psi)$coef[2, 1])-True_Psi); cat("\n Naives bias:", Naive_Bias)
Naives bias: 0.009269
Naive_Relative_Bias <- (((summary(Bias_Psi)$coef[2, 1])-True_Psi)/True_Psi)*100; cat("\n Relative Naives bias:", Naive_Relative_Bias,"%")
Relative Naives bias: 4.682 %
Data visualization
# DT table = interactive
# install.packages("DT") # install DT first
library(DT)
datatable(head(ObsData, n = nrow(ObsData)), options = list(pageLength = 5, digits = 2))
TMLE simple implementation
Step 1: \(Q_{0}\)(A,W)
Estimation of the initial probability of the outcome (Y) given the treatment (A) and the set of covariates (W), denoted as \(Q_{0}\)(A,W). To estimate \(Q_{0}\)(A,W) we can use a standard logistic regression model:
\[\text{logit}[P(Y=1|A,W)]\,=\,\beta_{0}\,+\,\beta_{1}A\,+\,\hat{\beta_{2}^{T}}W.\]
Therefore, we can estimate the initial probability as follows:
\[\bar{Q}^{0}(A,W)\,=\,\text{expit}(\hat{\beta_{0}}\,+\,\hat{\beta_{1}}A\,+\,\hat{\beta_{2}^{T}}W).\]
The predicted probability can be estimated using the Super-Learner library implemented in the R package “Super-Learner” (Van der Laan et al., 2007) to include any terms that are functions of A or W (e.g., polynomial terms of A and W, as well as the interaction terms of A and W, can be considered).
Consequently, for each subject, the predicted probabilities for both potential outcomes \(\bar{Q}^{0}(0,W)\) and \(\bar{Q}^{0}(1,W)\) can be estimated by setting A = 0 and A = 1 for everyone respectively: \[\bar{Q}^{0}(0,W)\,=\,\text{expit}(\hat{\beta_{0}}\,+\,\hat{\beta_{2}^{T}}W),\] and,
\[\bar{Q}^{0}(1,W)\,=\,\text{expit}(\hat{\beta_{0}}\,+\,\hat{\beta_{1}}A\,+\,\hat{\beta_{2}^{T}}W).\] Note: see appendix one for a short introduction to the Super-Learner and ensemble learning techniques.
ObsData <-subset(ObsData, select=c(w1,w2,w3,w4,A,Y))
Y <- ObsData$Y
A <- ObsData$A
w1 <- ObsData$w1
w2 <- ObsData$w2
w3 <- ObsData$w3
w4 <- ObsData$w4
m <- glm(Y ~ A + w1 + w2 + w3 + w4, family=binomial, data=ObsData)
Q <- cbind(QAW = predict(m),
Q1W = predict(m, newdata=data.frame(A = 1, w1, w2, w3, w4)),
Q0W = predict(m, newdata=data.frame(A = 0, w1, w2, w3, w4)))
Q0 <- as.data.frame(Q)
Y1<-Q0$Q1W
Y0<-Q0$Q0W
QA1<-exp(Y1)/(1+exp(Y1))
QA0<-exp(Y0)/(1+exp(Y0))
#Inverse logit (probability scale)
psi <- (exp(Y1)/(1+exp(Y1)) - exp(Y0)/(1+exp(Y0)))
Psi <- mean(exp(Y1)/(1+exp(Y1)) - exp(Y0)/(1+exp(Y0))); cat("\n Q0:", Psi)
Q0: 0.1992
df <- round(cbind(Logit=(Q),Pr.Y1=QA1,Pr.Y0=QA0,Psi=psi), digits= 3)
Visualizing the first step:
datatable(head(df, n = nrow(df)), options = list(pageLength = 5, digits = 3))
Step 2: \(g_{0}(A,W)\)
Estimation of the probability of the treatment (A) given the set of covariates (W), denoted as \(g_{0}(A,W)\). We can use again a logistic regression model and to improve the prediction algorithm we can use the Super-Learner library or any other machine learning strategy:
\[\text{logit}[P(A=1|W)]\,=\,\beta_{0}\,+\,\beta_{1}^{T}W.\] Then, we estimate the predicted probability of P(A|W) = \(\hat{g}(1,W)\) using:
\[\hat{g}(1,W)\,=\,\text{expit}\,(\hat{\beta_{0}}\,+\,\hat{\beta_{1}^{T}}W).\]
g <- glm(A ~ w2 + w3 + w4, family = binomial)
g1W = predict(g, type ="response");cat("\n Propensity score = g1W","\n");summary(g1W)
Propensity score = g1W
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.358 0.594 0.681 0.671 0.759 0.875
Step 3: HAW and \(\epsilon\)
This step aims to find a better prediction model targeted at minimising the mean squared error (MSE) for the potential outcomes. For the ATE on step convergence is guaranteed given \(\bar{Q}^{0}\) and \(\hat{g}(1,W)\).
The fluctuation parameters \((\hat{\epsilon}_{0}\,,\,\hat{\epsilon}_{1})\) are estimated using maximum likelihood procedures by setting \(\text{logit}(\bar{Q^{0}}(A,W))\) as an offset in a intercept-free logistic regression with \(H_{0}\) and \(H_{1}\) as independent variables:
\[\bar{Q^{1}}(A,W)\,=\,\text{expit}\left[\text{logit}\left(\bar{Q^{0}}(A, W)\right)\,+\,\hat{\epsilon_{0}}H_{0}(A,W)\,+\,\hat{\epsilon_{1}}H_{1}(A,W)\right] (5)\] \[\bar{Q^{1}}(0,W)\,=\,\text{expit}\left[\text{logit}\left(\bar{Q^{0}}(A,W)\right)\,+\,\hat{\epsilon_{0}}H_{0}(0,W)\right]\]
\[\bar{Q^{1}}(1,W)\,=\,\text{expit}\left[\text{logit}\left(\bar{Q^{0}}(A,W)\right)\,+\,\hat{\epsilon_{1}}H_{1}(1,W)\right]\] Where, \[H_{0}(A,W)\,=\,\frac{I(A=0)}{\hat{g}(0|W)}\;\text{and},\;H_{1}(A,W)\,=\,\frac{I(A=1)}{\hat{g}(1|W)}\] are the stabilized inverse probability of treatment (A) weights (IPTW), namelly the clever covariates and I defines an indicator function (note that \(\hat{g}(A|W)\) is estimted from step 2).
#Clever covariate and fluctuating/substitution paramteres
h <- cbind(gAW=(A/g1W -(1-A)/(1-g1W)), g1W=(1/g1W), g0W=(-1/(1-g1W)))
epsilon <- coef(glm(Y ~ -1 + h[,1] + offset(Q[,"QAW"]), family = binomial));cat("\n Epsilon:",epsilon)
Epsilon: 0.001189
df <- round(cbind(Q0,PS=(g1W),H=(h),epsilon), digits= 4)
Visualizing the 3rd step (PS = propensity score; H = IPTW or clever covarites):
datatable(head(df, n = nrow(df)), options = list(pageLength = 5, digits = 3))
Step 4 \(\bar{Q_{n}}^{*}:\text{from}\,\bar{Q_{0}}^{0}\,\text{to}\,\bar{Q_{1}}^{1}\)
Afterwards, the estimated probability of the potential outcomes is updated by the substitution parameters \((\hat{\epsilon_{0}}\,,\,\hat{\epsilon_{1}})\). The substitution update is performed by setting A = 0 and A = 1 for each subject in the initial estimate probability of the potential outcomes \(\bar{Q^{0}}(0,W)\,,\,\bar{Q^{0}}(1,W)\), as well as in the clever covariates \(H_{0}(0,W)\;\text{and}\; H_{1}(1,W)\).
For the \(\Psi(\bar{Q_{n}}^{*})\), the updated estimate of the potential outcomes only needs one iteration \(\Psi(\bar{Q_{n}}^{*})\) from \(\bar{Q}^{0}(A,W)\,=>\bar{Q^{1}}(A,W)\). Therefore, model (5) targets \(E[\hat{Y}_{A=0}]\;\text{and}\; E[\hat{Y}_{A=1}]\) simultaneously by including both \(H_{0}(A,W)\,\text{and}\,H_{1}(A,W)\) in the model. Hence \(\psi\) is finally estimated as follows:
\[\psi TMLE,n = \Psi(Q_{n}^{*})= {\frac{1}{n}\sum_{i=1}^{n}\bar{Q}_{n}^{1}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{1}\left(0,\ W_{i}\right)}. (1)\]
Qstar <- plogis(Q + epsilon*h)
psi <- (Qstar[,"Q1W"] - Qstar[,"Q0W"])
Psi <- mean(Qstar[,"Q1W"] - Qstar[,"Q0W"]);
cat("TMLE_Psi:", Psi)
TMLE_Psi: 0.2004
cat("\n TMLE.SI_bias:", abs(True_Psi-Psi))
TMLE.SI_bias: 0.002383
cat("\n Relative_TMLE.SI_bias:",abs(True_Psi-Psi)/True_Psi*100,"%")
Relative_TMLE.SI_bias: 1.204 %
Visualizing the 4th step (H = IPTW or clever covarites):
df <- round(cbind(Q0=(Q0),H=(h),epsilon,psi), digits= 4)
datatable(head(df, n = nrow(df)), options = list(pageLength = 5, digits = 3))
cat("\n Psi first row:", plogis((0.001189*1.2399)+(2.3678))-(plogis((0.001189*-5.1681)+(1.3413))))
Psi first row: 0.1228
cat("\n TMLE_Psi:", Psi)
TMLE_Psi: 0.2004
Step 5: Inference
TMLE uses the efficient influence curve (IC) for inference (i.e., to obtain standard errors for \(\psi\)).
\[IC_{n}(O_{i})\ \ =\ \left(\frac{I\left(A_{i}=1\right)}{g_n\left(1\left|W_{i}\right)\right)}\ -\ \frac{I\left(A_{i}=0\right)}{g_n\left(0\left|W_{i}\right)\right)}\ \right)\left[Y_{i}-\bar{Q}_{n}^{1}\left(A_{i},W_{i}\right)\right]+\bar{Q}_{n}^{1}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{1}\left(0,\ W_{i}\right) - \psi TMLE,n. (2)\]
where the standard deviation for \(\psi\) is estimated as follows:
\[\sigma({\psi_{0}})=\sqrt{\frac{Var(IC_{n})}{n}}. (3)\] Note: see appendix two for a short introduction to the theory of the Influence Curve.
Q <- as.data.frame(Q)
IC <- h[,1]*(Y-Q$QAW) + Q$Q1W - Q$Q0W - Psi;summary(IC)
Min. 1st Qu. Median Mean 3rd Qu. Max.
-2.950 -0.855 -0.200 -0.006 0.570 14.300
n <- nrow(ObsData)
varHat.IC <- var(IC)/n; varHat.IC
[1] 0.0002024
#Psi and 95%CI for Psi
cat("\n TMLE_Psi:", Psi)
TMLE_Psi: 0.2004
cat("\n 95%CI:", c(Psi-1.96*sqrt(varHat.IC), Psi+1.96*sqrt(varHat.IC)))
95%CI: 0.1725 0.2283
cat("\n TMLE.SI_bias:", abs(True_Psi-Psi))
TMLE.SI_bias: 0.002383
cat("\n Relative_TMLE.SI_bias:",abs(True_Psi-Psi)/True_Psi*100,"%")
Relative_TMLE.SI_bias: 1.204 %
TMLE vs. AIPTW
The advantages of TMLE have repeatedly been demonstrated in both simulation studies and applied analyses (Laan and Rose, 2011).
Evidence shows that TMLE provides the less unbiased ATE estimate compared with other double-robust estimators (Neugebauer and Laan, 2005), (Laan and Rose, 2011) such as the combination of regression adjustment with inverse probability of treatment weighting (IPTW-RA) and the augmented inverse probability of treatment weighting (AIPTW). The AIPTW estimation is a two-step procedure with two equations (propensity score and mean outcome equations).
To estimate the ATE using the AIPTW estimator one can set the estimation equation (EE) (4) equal to zero and use bootstrap to derive 95% confidence intervals (CI). However, solving the EE using the generalized method of moments (GMM), stacking both equations (propensity score and outcome), reduces the estimation and inference steps to only one. However, given that the propensity score in equation (4) can easily fall outside the range [0, 1] (if for some observations \(g_{n}(1|W_{i})\) is close to 1 or 0) the AIPTW estimation can be unstable (near violation of the positivity assumption). AIPTW instability under near violation of the positivity assumption represents the price of not being a substitution estimator as TMLE.
\[\psi_{0}^{AIPTW-ATE}\ \ =\ \frac{1}{n}\sum_{i=1}^{n}\left(\frac{I\left(A_{i}=1\right)}{g_n\left(1\left|W_{i}\right)\right)}\ -\ \frac{I\left(A_{i}=0\right)}{g_n\left(0\left|W_{i}\right)\right)}\ \right)\left[Y_{i}-\bar{Q}_{n}^{0}\left(A_{i},W_{i}\right)\right]+\frac{1}{n}\sum_{i=1}^{n}\bar{Q}_{n}^{0}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{0}\left(0,\ W_{i}\right). (4)\]
AIPTW <- mean((h[,1]*(Y-Q$QAW))+(Q$Q1W-Q$Q0W)); AIPTW
[1] 0.1948
cat("\n AIPTW_bias:", abs(True_Psi-AIPTW))
AIPTW_bias: 0.003211
cat("\n Relative_AIPTW_bias:",abs(True_Psi-AIPTW)/True_Psi*100,"%")
Relative_AIPTW_bias: 1.622 %
Compared with AIPTW, TMLE showed smaller relative bias.
TMLE using the Super-Learner
With TMLE we can call the Super-Learner (SL). The SL is a R-package using V-fold cross-validation and ensembled learning (prediction using all the predictions of multiple stacked learning algorithms) techniques to improve model prediction performance (Breiman, 1996).
The basic implementation of TMLE in the R-package tmle uses by default three algorithms:
1. SL.glm (main terms logistic regression of A and W),
2. SL.step (stepwise forward and backward model selection using AIC criterion, restricted to second order polynomials) and,
3. SL.glm.interaction (a glm variant that includes second order polynomials and two by two interactions of the main terms included in the model).
The principal interest of calling the Super-Learner is to obtain the less-unbiased estimated for \(\bar Q_{n}^{0}(A,W)\) and \(g_{0}(A,W)\). It is achieved by obtaining the smallest expected loss function for Y or A (binary outcomes), respectively. For instance, the negative logarithmic loss function for Y is computed as the minimizer of the expected squared error loss:
\[\bar Q_{0}\,=\, \text{arg min}_{\bar Q}E_{0}L(O, \bar Q),\]
where \(L(O, \bar Q)\) is: \[ (Y \,-\, \bar Q(A, W))^{2}\] Note: see appendix one for a short introduction to the Super-Learner and ensemble learning techniques.
- Step One: \(\bar Q_{n}^{0}(A,W)\) prediction
#E(Y|A,W) prediction
library(SuperLearner)
#Specify SuperLearner libraries
SL.library <- c("SL.glm","SL.step","SL.glm.interaction")
#Data frame with X with baseline covariates and exposure A
X <- subset(ObsData, select=c(A, w1, w2, w3, w4))
n <- nrow(ObsData)
#Create data frames with A=1 and A=0
X1<-X0<-X
X1$A <-1
X0$A <-0
#Create new data by stacking X, X1, and X0
newdata <- rbind(X,X1,X0)
#Call superlearner
Qinit <- SuperLearner(Y=ObsData$Y, X=X, newX=newdata, SL.library=SL.library, family="binomial")
Qinit
Call:
SuperLearner(Y = ObsData$Y, X = X, newX = newdata, family = "binomial",
SL.library = SL.library)
Risk Coef
SL.glm_All 0.1766 0.6002
SL.step_All 0.1766 0.0000
SL.glm.interaction_All 0.1767 0.3998
#Predictions
#Pred prob of mortality (Y) given A, W
QbarAW <- Qinit$SL.predict[1:n]
#Pred prob of dying for each subject given A=1 and w
Qbar1W <- Qinit$SL.predict[(n+1):(2*n)]
#Pred prob of dying for each subject given A=0 and w
Qbar0W <- Qinit$SL.predict[(2*n+1):(3*n)]
#Simple substitution estimator Psi(Q0)
PsiHat.SS <- mean(Qbar1W-Qbar0W);PsiHat.SS
[1] 0.199
- Step two: \(g_{0}(A,W)\) prediction
#Step 2 g_0(A|W) with SuperLearner
w <- subset(ObsData, select=c(w1,w2,w3,w4))
gHatSL <- SuperLearner(Y=ObsData$A, X=w, SL.library=SL.library, family = binomial)
gHatSL;mean(gHatSL)
Call:
SuperLearner(Y = ObsData$A, X = w, family = binomial, SL.library = SL.library)
Risk Coef
SL.glm_All 0.2091 0.0000
SL.step_All 0.2091 0.3803
SL.glm.interaction_All 0.2090 0.6197
[1] NA
#Generate the pred prob of A=1 and, A=0 given covariates
gHat1W <- gHatSL$SL.predict
gHat0W <- 1-gHat1W
#Step 3: Clever covariate
HAW <- as.numeric(ObsData$A==1)/gHat1W - as.numeric(ObsData$A==0)/gHat0W;mean(HAW)
[1] 0.002954
H1W <- 1/gHat1W
H0W <- -1/gHat0W
- Steps 3 and 4: fluctuation step and substitution estimation for \(\bar Q_{n}^{0}(A,W)\) to \(\bar Q_{n}^{1}(A,W)\)
#Step 4: Substitution estimaiton Q* of the ATE.
logitUpdate <- glm(ObsData$Y ~ -1 + offset(qlogis(QbarAW))+HAW, family='binomial')
eps <- logitUpdate$coef;eps
HAW
0.0004483
#Calculating the predicted values for each subject under each treatment A=1, A=0
QbarAW.star <- plogis(qlogis(QbarAW)+eps*HAW)
Qbar1W.star <- plogis(qlogis(Qbar1W)+eps*H1W)
Qbar0W.star <- plogis(qlogis(Qbar0W)+eps*H0W)
PsiHat.TMLE.SL <- mean(Qbar1W.star) - mean(Qbar0W.star)
cat("PsiHat.TMLE.SL:", PsiHat.TMLE.SL)
PsiHat.TMLE.SL: 0.1995
cat("\n PsiHat.TMLE.SL_bias:", abs(True_Psi-PsiHat.TMLE.SL))
PsiHat.TMLE.SL_bias: 0.001456
cat("\n Relative_PsiHat.TMLE.SL_bias:",abs(True_Psi-PsiHat.TMLE.SL)/True_Psi*100,"%")
Relative_PsiHat.TMLE.SL_bias: 0.7354 %
R-TMLE
Using the R-package tmle.
The basic implementation of TMLE in the R-package tmle uses by default three algorithms:
1. SL.glm (main terms logistic regression of A and W),
2. SL.step (stepwise forward and backward model selection using AIC criterion, restricted to second order polynomials) and,
3. SL.glm.interaction (a glm variant that includes second order polynomials and two by two interactions of the main terms included in the model).
library(tmle)
w <- subset(ObsData, select=c(w1,w2,w3,w4))
tmle <- tmle(Y, A, W=w)
cat("TMLER_Psi:", tmle$estimates[[2]][[1]],";","95%CI(", tmle$estimates[[2]][[3]],")")
TMLER_Psi: 0.1994 ; 95%CI( 0.1799 0.2189 )
cat("\n TMLE_bias:", abs(True_Psi-tmle$estimates[[2]][[1]]))
TMLE_bias: 0.001407
cat("\n Relative_TMLE_bias:",abs(True_Psi-tmle$estimates[[2]][[1]])/True_Psi*100,"%")
Relative_TMLE_bias: 0.7108 %
R-TMLE improving prediction
In addition to the default algorithms implemented in the R-tmle package, we can improve our estimation calling more efficient machine learing algorithms, such as generalized additive models, the Random Forest and, Recursive Partitioning and Regression Trees:
SL.TMLER.Psi <- tmle(Y=Y, A=A, W=w, family="binomial",
Q.SL.library = c("SL.glm", "SL.step", "SL.glm.interaction", "SL.gam", "SL.randomForest", "SL.rpart"),
g.SL.library = c("SL.glm", "SL.step", "SL.glm.interaction", "SL.gam", "SL.randomForest", "SL.rpart"))
Loading required package: gam
Loading required package: splines
Loading required package: foreach
foreach: simple, scalable parallel programming from Revolution Analytics
Use Revolution R for scalability, fault tolerance and more.
http://www.revolutionanalytics.com
Loaded gam 1.14
Loading required package: randomForest
randomForest 4.6-12
Type rfNews() to see new features/changes/bug fixes.
Loading required package: rpart
cat("SL.TMLER.Psi:", SL.TMLER.Psi$estimates[[2]][[1]],";","95%CI(", SL.TMLER.Psi$estimates[[2]][[3]],")")
SL.TMLER.Psi: 0.1994 ; 95%CI( 0.1799 0.2188 )
cat("\n SL.TMLER.Psi_bias:", abs(True_Psi-SL.TMLER.Psi$estimates[[2]][[1]]))
SL.TMLER.Psi_bias: 0.001362
cat("\n Relative_SL.TMLER.Psi_bias:",abs(True_Psi-SL.TMLER.Psi$estimates[[2]][[1]])/True_Psi*100,"%")
Relative_SL.TMLER.Psi_bias: 0.6881 %
Conclusions
We have demonstrated:
- TMLE excels the AIPTW estimator and,
- TMLE best performance is obtained when calling more advanced Super-Learner algorithms.
Appendix One
Efron in 1982 showed that the empirical Influence Curve estimate of standard error is the same as the one obtained using the infinitesimal jackknife and the nonparametric delta method (Efron and Efron, 1982).
- The Delta Method uses the first order of the Taylor series expansion: \[f(x) \approx f(x)\,-\,f(\mu)\,+\,(x\,-\,\mu)f'(\mu);\]
where \(f'(\mu)\) is the derivative of the function with respect to X evaluated at the mean of X. Therefore, squaring both terms, the variance is approximately estimate as follwos: \[\text{E}[f(x)-f(\mu)]^{2}\approx \text{E}(x\,-\,\mu)^{2}\,\times\,[f'(\mu)]^{2};\] The left-hand side of the above equation is approximately the variance of \(f(x)\) and applied to the empirical distribution of X, the sample estimate of variance for X replaces: \[\text{E}(x_{i}\,-\,\mu)^{2}.\] The infinitesimal jackknife estimate of the standard error is defined as follows: \[SD_{ij}(\theta_{e})\,=\,\left(\frac{\sum_{i=1}^{n}U_{i}^{2}}{n^{2}}\right)^{1/2};\] where \(\theta_{e}\) is the estimate of the parameter \(\theta\) and \(U_{i}\) is a directional derivative in the direction of the ith coordinate centered at the mean of the empirical distribution function.
Figure 3. Estimate of the \(\psi\) Standard Error using the efficient Influence Curve.
Image credit: Miguel Angel Luque-Fernandez.
Appendix Two
With TMLE we can call the R-package Super-Learner (SL). The SL uses cross-validation and ensembled learning (using all the predictions of multiple stacked learning algorithms) techniques to improve model prediction performance (Breiman, 1996).
The SL algorithm provides a system based on V-fold cross-validation (Efron and Gong, 1983) (10-folds) to combine adaptively multiple algorithms into an improved estimator, and returns a function than can be used for prediction in new datasets.
Figure 4: 10-fold cross-validation algorithm.
The basic implementation of TMLE in the R-package tmle uses by default three algorithms:
1. SL.glm (main terms logistic regression of A and W),
2. SL.step (stepwise forward and backward model selection using AIC criterion, restricted to second order polynomials) and,
3. SL.glm.interaction (a glm variant that includes second order polynomials and two by two interactions of the main terms included in the model).
The principal interest of calling the Super-Learner is to obtain the less-unbiased estimated for \(\bar Q_{n}^{0}(A,W)\) and \(g_{0}(A,W)\). It is achieved by obtaining the smallest expected loss function for Y or A (binary outcomes), respectively. For instance, the negative logarithmic loss function for Y is computed as the minimizer of the expected squared error loss:
\[\bar Q_{0}\,=\, \text{arg min}_{\bar Q}E_{0}L(O, \bar Q),\]
where \(L(O, \bar Q)\) is: \[ (Y \,-\, \bar Q(A, W))^{2}\] The SL algorithm first split the data into ten blocks and fits each of the selected algoriths on the training set (non-shaded blocks), then predicts the estimated probabilities of the outcome (Y) using the validation set (shaded block) for each algorithm, based on the corresponding training set. Afterwards, the SL estimates the the cross-validating risk for each algorithm averaging the risks across validation sets resulting in one estimated cross-validated risk for each algorithm. Finally, the SL selects the combination of Z that minimises the cross-validation risk, defined as the minimum mean square error for each of the selected algorithms using Y and Z. A weighted combination of the algorithms (ensemble learning) in Z is then used to predict the outcome (Y) (see Figure 5).
Figure 5: Flow Diagram for the Super-Learner algorithm.
Adapted from: Mark van der Laan and Sherri Rose. Targeted learning: causal inference for observational and experimental dataSpringer Series in Statistics, 2011.
Abbreviations
TMLE: Targeted maximum likelihood estimation
SL: Super Learner
IPTW: Inverse probability of treatment weighting
AIPTW: Augmented inverse probability of treatment weighting
MSE: Mean squared error
SE: Standard error
EE: Estimation equations
GMM: Generalised method of moments
O: Observed ordered data structure
W: Vector of covariates
A: Binary treatment or exposure
Y: Binary outcome
\(Y_{1}, Y_{O}\): Counterfactual outcomes with binary treatment A
\(P_{0}\): True data-generating distribution
\(\Psi(P_{0})\):True target parameter
\(\psi_{0}\,=\,\Psi(P_{0})\): True target parameter value
\(g_{0}\): Propensity score for the treatment mechanism (A)
\(g_{0}\): Estimate of \(g_{0}\)
\(\epsilon\): Fluctuation parameter
\(\epsilon_{n}\): Estimate of \(\epsilon\) \(H_{n}^{*}\): Clever covariate estimate (inverse probability of treatment weight)
\(L(O,\bar Q)\): Example of a loss function where it is a function of O and \(\bar Q\)
\((Y \,-\, \bar Q(A, W))^{2}\): Expected squared error loss
\(\bar Q_{0}\): Conditional mean of outcome given parents; \(E_{0}(Y|A,W)\)
\(\bar Q_{n}\): Estimate of \(\bar Q_{0}\)
\(\bar Q_{n}^{0}\): Initial estimate of \(\bar Q_{0}\)
\(\bar Q_{n}^{1}\): First updated estimate of \(\bar Q_{0}\)
\(\bar Q_{n}^{*}\): Targeted estimate of \(\bar Q_{n}^{0}\) in TMLE procedure; \(\bar Q_{n}^{*}\) may equal \(\bar Q_{n}^{1}\)
Session Info
devtools::session_info()
Session info ---------------------------------------------------
setting value
version R version 3.3.2 (2016-10-31)
system x86_64, darwin13.4.0
ui RStudio (1.0.44)
language (EN)
collate en_US.UTF-8
tz Europe/London
date 2016-11-07
Packages -------------------------------------------------------
package * version date
assertthat 0.1 2013-12-06
base64enc 0.1-3 2015-07-28
codetools 0.2-15 2016-10-05
devtools 1.12.0 2016-06-24
digest 0.6.10 2016-08-02
DT * 0.2 2016-08-09
evaluate 0.10 2016-10-11
foreach * 1.4.3 2015-10-13
formatR 1.4 2016-05-09
gam * 1.14 2016-09-10
htmltools 0.3.5 2016-03-21
htmlwidgets 0.7 2016-08-02
iterators 1.0.8 2015-10-13
jsonlite 1.1 2016-09-14
knitr 1.14 2016-08-13
magrittr 1.5 2014-11-22
memoise 1.0.0 2016-01-29
nnls * 1.4 2012-03-19
randomForest * 4.6-12 2015-10-07
Rcpp 0.12.7 2016-09-05
rmarkdown 1.1.9007 2016-10-25
rpart * 4.1-10 2015-06-29
rprojroot 1.0-2 2016-03-28
stringi 1.1.2 2016-10-01
stringr 1.1.0 2016-08-19
SuperLearner * 2.0-19 2016-02-04
tibble 1.2 2016-08-26
tmle * 1.2.0-4 2014-03-09
withr 1.0.2 2016-06-20
yaml 2.1.13 2014-06-12
source
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.2)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
Github (rstudio/rmarkdown@746d0eb)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
CRAN (R 3.3.0)
Thank you
Thank you for participating in this tutorial.
If you have updates or changes that you would like to make, please send me a pull request. Alternatively, if you have any questions, please e-mail me. You can cite this repository as:
Luque-Fernandez MA, (2016). Taregeted Maximum Likelihood Estimation for a Binary Outcome: Tutorial and Guided Implementation. GitHub repository, http://migariane.github.io/TMLE.nb.html.
Miguel Angel Luque Fernandez
E-mail: miguel-angel.luque at lshtm.ac.uk
Twitter @WATZILEI
References
Breiman L. (1996). Stacked regressions. Machine learning 24: 49–64.
Bühlmann P, Drineas P, Laan M van der, Kane M. (2016). Handbook of big data. CRC Press.
Efron B, Efron B. (1982). The jackknife, the bootstrap and other resampling plans. SIAM.
Efron B, Gong G. (1983). A leisurely look at the bootstrap, the jackknife, and cross-validation. The American Statistician 37: 36–48.
Greenland S, Robins JM. (1986). Identifiability, exchangeability, and epidemiological confounding. International journal of epidemiology 15: 413–419.
Gruber S, Laan M van der. (2011). Tmle: An r package for targeted maximum likelihood estimation. UC Berkeley Division of Biostatistics Working Paper Series.
Hampel FR. (1974). The influence curve and its role in robust estimation. Journal of the American Statistical Association 69: 383–393.
Laan M van der, Rose S. (2011). Targeted learning: Causal inference for observational and experimental data. Springer Series in Statistics.
Laan MJ van der, Rubin D. (2006). Targeted maximum likelihood learning. The International Journal of Biostatistics 2.
Lunceford JK, Davidian M. (2004). Stratification and weighting via the propensity score in estimation of causal treatment effects: A comparative study. Statistics in Medicine 23: 2937–2960.
Neugebauer R, Laan M van der. (2005). Why prefer double robust estimators in causal inference? Journal of Statistical Planning and Inference 129: 405–426.
Robins JM, Hernan MA, Brumback B. (2000). Marginal structural models and causal inference in epidemiology. Epidemiology 550–560.
Rosenbaum PR, Rubin DB. (1983). The central role of the propensity score in observational studies for causal effects. Biometrika 70: 41–55.
Rubin DB. (2011). Causal inference using potential outcomes. Journal of the American Statistical Association.
Rubin DB. (1974). Estimating causal effects of treatments in randomized and nonrandomized studies. Journal of educational Psychology 66: 688.
Van der Laan MJ, Polley EC, Hubbard AE. (2007). Super learner. Statistical applications in genetics and molecular biology 6.
LS0tCnRpdGxlOiAiVGFyZ2V0ZWQgTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24gZm9yIGEgQmluYXJ5IE91dGNvbWU6IFR1dG9yaWFsIGFuZCBHdWlkZWQgSW1wbGVtZW50YXRpb24iCmF1dGhvcjogJ0J5OiBNaWd1ZWwgQW5nZWwgTHVxdWUgRmVybmFuZGV6JwpkYXRlOiAiTm92ZW1iZXIgOHRoLCAyMDE2IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgaGlnaGxpZ2h0OiBkZWZhdWx0CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IGpvdXJuYWwKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogbm8KICAgICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICAgIHRvY19kZXB0aDogMwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCmZvbnQtZmFtaWx5OiBSaXNxdWUKZm9udC1pbXBvcnQ6IGh0dHA6Ly9mb250cy5nb29nbGVhcGlzLmNvbS9jc3M/ZmFtaWx5PVJpc3F1ZQpjc2w6IHJlZmVyZW5jZXMvaXNtZS5jc2wKYmlibGlvZ3JhcGh5OiByZWZlcmVuY2VzL2JpYmxpb2dyYXBoeS5iaWIKLS0tCgo8IS0tQkVHSU46ICBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGFuZCBsb2FkIHBhY2thZ2VzLS0+CmBgYHtyIHNldC1nbG9iYWwtb3B0aW9ucywgZWNobyA9IEZBTFNFLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICBlY2hvID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICBjYWNoZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZGVwZW5kc29uID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgIGVuZ2luZSA9ICJSIiwgIyBDaHVua3Mgd2lsbCBhbHdheXMgaGF2ZSBSIGNvZGUsIHVubGVzcyBub3RlZAogICAgICAgICAgICAgICAgICAgICAgZXJyb3IgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5wYXRoPSJGaWd1cmVzLyIsICAjIFNldCB0aGUgZmlndXJlIG9wdGlvbnMKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCAKICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDcsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gNykKI1lvdSBuZWVkIHRoZSBzdWdnZXN0ZWQgcGFja2FnZXMgdG8gcnVuIHRoaXMgbm90ZWJvb2sKI2luc3RhbGwucGFja2FnZXMoJ3RtbGUnLCAnU3VwZXJMZWFybmVyJywgJ0RUJykKbGlicmFyeSgndG1sZScsICdTdXBlckxlYXJuZXInLCAnRFQnKQpgYGAKCiNJbnRyb2R1Y3Rpb24KCkR1cmluZyB0aGUgbGFzdCAzMCB5ZWFycywgKiptb2Rlcm4gZXBpZGVtaW9sb2d5KiogaGFzIGJlZW4gYWJsZSB0byBpZGVudGlmeSBzaWduaWZpY2FudCBsaW1pdGF0aW9ucyBvZiBjbGFzc2ljIGVwaWRlbWlvbG9naWMgbWV0aG9kcyB3aGVuIHRoZSBmb2N1cyBpcyB0byBleHBsYWluIHRoZSBtYWluIGVmZmVjdCBvZiBhIHJpc2sgZmFjdG9yIG9uIGEgZGlzZWFzZSBvciBvdXRjb21lLiAgCgpDYXVzYWwgSW5mZXJlbmNlIGJhc2VkIG9uIHRoZSAqKk5leW1hLVJ1YmluIFBvdGVudGlhbCBPdXRjb21lcyBGcmFtZXdvcmsqKiBbQHJ1YmluMjAxMV0sIGZpcnN0IGludHJvZHVjZWQgaW4gU29jaWFsIFNjaWVuY2UgYnkgRG9uYWwgUnViaW4gW0BydWJpbjE5NzRdIGFuZCBsYXRlciBpbiBFcGlkZW1pb2xvZ3kgYW5kIEJpb3N0YXRpc3RpY3MgYnkgSmFtZXMgUm9iaW5zIFtAcm9iaW5zMTk4Nl0sIGhhcyBwcm92aWRlZCB0aGUgdGhlb3J5IGFuZCBzdGF0aXN0aWNhbCBtZXRob2RzIG5lZWRlZCB0byBvdmVyY29tZSByZWN1cnJlbnQgcHJvYmxlbXMgaW4gb2JzZXJ2YXRpb25hbCBlcGlkZW1pb2xvZ2ljIHJlc2VhcmNoLCBzdWNoIGFzOgoKMS4gbm9uLWNvbGxhcHNpYmlsaXR5IG9mIHRoZSBvZGRzIGFuZCBoYXphcmQgcmF0aW9zLAoyLiBpbXBhY3Qgb2YgcGFyYWRveGljYWwgZWZmZWN0cyBkdWUgdG8gY29uZGl0aW9uaW5nIG9uIGNvbGxpZGVycywKMy4gc2VsZWN0aW9uIGJpYXMgcmVsYXRlZCB0byB0aGUgdmFndWUgdW5kZXJzdGFuZGluZyBvZiB0aGUgZWZmZWN0IG9mIHRpbWUgb24gZXhwb3N1cmUgYW5kIG91dGNvbWUgYW5kLAo0LiBlZmZlY3Qgb2YgdGltZS1kZXBlbmRlbnQgY29uZm91bmRpbmcgYW5kIG1lZGlhdG9ycywKNS4gZXRjLgoKQ2F1c2FsIGVmZmVjdHMgYXJlIG9mdGVuIGZvcm11bGF0ZWQgcmVnYXJkaW5nIGNvbXBhcmlzb25zIG9mIHBvdGVudGlhbCBvdXRjb21lcywgYXMgZm9ybWFsaXNlZCBieSBSdWJpbiBbQHJ1YmluMjAxMV0uIExldCBBIGRlbm90ZSBhIGJpbmFyeSBleHBvc3VyZSwgKipXKiogYSB2ZWN0b3Igb2YgcG90ZW50aWFsIGNvbmZvdW5kZXJzLCBhbmQgWSBhIGJpbmFyeSBvdXRjb21lLiBHaXZlbiBBLCBlYWNoIGluZGl2aWR1YWwgaGFzIGEgcGFpciBvZiBwb3RlbnRpYWwgb3V0Y29tZXM6IHRoZSBvdXRjb21lIHdoZW4gZXhwb3NlZCwgZGVub3RlZCAkWV97MX0kLCBhbmQgdGhlIG91dGNvbWUgd2hlbiB1bmV4cG9zZWQsICRZX3swfSQuIFRoZXNlIHF1YW50aXRpZXMgYXJlIHJlZmVycmVkIHRvIGFzICoqcG90ZW50aWFsIG91dGNvbWVzKiogc2luY2UgdGhleSBhcmUgaHlwb3RoZXRpY2FsLCBnaXZlbiB0aGF0IGl0IGlzIG9ubHkgcG9zc2libGUgdG8gb2JzZXJ2ZSBhIHNpbmdsZSByZWFsaXNhdGlvbiBvZiB0aGUgb3V0Y29tZSBmb3IgYW4gaW5kaXZpZHVhbDsgd2Ugb2JzZXJ2ZSAkWV97MX0kIG9ubHkgZm9yIHRob3NlIGluIHRoZSBleHBvc3VyZSBncm91cCBhbmQgJFlfezB9JCBvbmx5IGZvciB0aG9zZSBpbiB0aGUgdW5leHBvc2VkIGdyb3VwIFtAcnViaW4xOTc0XS4gQSBjb21tb24gY2F1c2FsIGVzdGltYW5kIGlzIHRoZSAqKkF2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdCoqIChBVEUpLCBkZWZpbmVkIGFzICRFW1lfezF9XCwg4oCTIFwsWV97MH1dJC4gIAoKQ2xhc3NpY2FsIGVwaWRlbWlvbG9naWMgbWV0aG9kcyB1c2UgcmVncmVzc2lvbiBhZGp1c3RtZW50IHRvIGV4cGxhaW4gdGhlIG1haW4gZWZmZWN0IG9mIGEgcmlzayBmYWN0b3IgbWVhc3VyZSBvbiBhIGRpc2Vhc2Ugb3Igb3V0Y29tZS4gUmVncmVzc2lvbiBhZGp1c3RtZW50IGNvbnRyb2wgZm9yIGNvbmZvdW5kaW5nIGJ1dCByZXF1aXJlcyBtYWtpbmcgdGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgZWZmZWN0IG1lYXN1cmUgaXMgY29uc3RhbnQgYWNyb3NzIGxldmVscyBvZiBjb25mb3VuZGVycyBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuIEhvd2V2ZXIsIGluIG5vbi1yYW5kb21pemVkIG9ic2VydmF0aW9uYWwgc3R1ZGllcywgdGhlIGVmZmVjdCBtZWFzdXJlIGlzIG5vdCBjb25zdGFudCBhY3Jvc3MgZ3JvdXBzIGdpdmVuIHRoZSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9uIG9mIGluZGl2aWR1YWwgY2hhcmFjdGVyaXN0aWNzIGF0IGJhc2VsaW5lLiAgCgpKYW1lcyBSb2JpbnMgaW4gMTk4NiBkZW1vbnN0cmF0ZWQgdGhhdCB1c2luZyB0aGUgKipHLWZvcm11bGEqKiBhIGdlbmVyYWxpemF0aW9uIG9mIHRoZSAqKnN0YW5kYXJkaXNhdGlvbioqLCBhbGxvd3Mgb2J0YWluaW5nIGEgdW5jb25mb3VuZGVkIG1hcmdpbmFsIGVzdGltYXRpb24gb2YgdGhlIEFURSB1bmRlciBjYXVzYWwgdW50ZXN0YWJsZSBhc3N1bXB0aW9ucywgbmFtZWx5IGNvbmRpdGlvbmFsIG1lYW4gaW5kZXBlbmRlbmNlLCBwb3NpdGl2aXR5IGFuZCBjb25zaXN0ZW5jeSBvciBzdGFibGUgdW5pdCB0cmVhdG1lbnQgdmFsdWUgYXNzaWdubWVudCAoU1VUVkEpIFtAcm9iaW5zMTk4Nl0sIFtAcm9iaW5zMjAwMF06ICAgICAgCiAgCiNUaGUgRy1Gb3JtdWxhIGFuZCBBVEUgZXN0aW1hdGlvbgoKJCRccHNpKFBfezB9KVwsPVwsXHN1bV97d31cLFxsZWZ0W1xzdW1fe3l9XCxQKFk9eVxtaWQgQT0xLFc9dyktXCxcc3VtX3t5fVwsUChZID0geVxtaWQgQT0wLFc9dylccmlnaHRdUChXPXcpJCQgIAoKd2hlcmUsICAgCgokJFAoWSA9IHkgXG1pZCBBID0gYSwgVyA9IHcpXCw9XCxcZnJhY3tQKFcgPSB3LCBBID0gYSwgWSA9IHkpfXtcc3VtX3t5fVwsUChXID0gdywgQSA9IGEsIFkgPSB5KX0kJCAgICAgCmlzIHRoZSBjb25kaXRpb25hbCBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gb2YgWSA9IHksIGdpdmVuIEEgPSBhLCBXID0gdyBhbmQsIAoKJCRQKFcgPSB3KVwsPVwsXHN1bV97eSxhfVwsUChXID0gdywgQSA9IGEsIFkgPSB5KSQkIAoKVGhlIEFURSBjYW4gYmUgZXN0aW1hdGVkICoqbm9uLXBhcmFtZXRyaWNhbGx5KiogdXNpbmcgdGhlIEctZm9ybXVsYS4gSG93ZXZlciwgdGhlICoqY291cnNlIG9mIGRpbWVuc2lvbmFsaXR5KiogaW4gb2JzZXJ2YXRpb25hbCBzdHVkaWVzIGxpbWl0cyBpdHMgZXN0aW1hdGlvbi4gSGVuY2UsIHRoZSBlc3RpbWF0aW9uIG9mIHRoZSBBVEUgdXNpbmcgdGhlIEctZm9ybXVsYSByZWxpZXMgbW9zdGx5IG9uICoqcGFyYW1ldHJpYyBtb2RlbGxpbmcqKiBhbmQgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24uICAgCgpUaGUgY29ycmVjdCBtb2RlbCBzcGVjaWZpY2F0aW9uIGluIHBhcmFtZXRyaWMgbW9kZWxsaW5nIGlzIGNydWNpYWwgdG8gb2J0YWluIHVuYmlhc2VkIGVzdGltYXRlcyBvZiB0aGUgdHJ1ZSBBVEUgW0BydWJpbjIwMTFdLiBBbHRlcm5hdGl2ZWx5LCBwcm9wZW5zaXR5IHNjb3JlIG1ldGhvZHMsIGludHJvZHVjZWQgYnkgUm9zZW5iYXVtIGFuZCBSdWJpbiBbQHJvc2VuYmF1bTE5ODNdLCBhcmUgYWxzbyBjb21tb25seSB1c2VkIGZvciBlc3RpbWF0aW9uIG9mIHRoZSBBVEUuIFRoZSBwcm9wZW5zaXR5IHNjb3JlIGlzIGEgYmFsYW5jaW5nIHNjb3JlIHRoYXQgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIHN0YXRpc3RpY2FsbHkgZXF1aXZhbGVudCBleHBvc3VyZSBncm91cHMgdG8gZXN0aW1hdGUgdGhlIEFURSB2aWEgbWF0Y2hpbmcsIHdlaWdodGluZywgb3Igc3RyYXRpZmljYXRpb24gW0Byb3NlbmJhdW0xOTgzXS4gCgpIb3dldmVyLCB2ZXJ5IGxvdyBvciB2ZXJ5IGhpZ2ggcHJvcGVuc2l0eSBzY29yZXMgY2FuIGxlYWQgdG8gdmVyeSBsYXJnZSB3ZWlnaHRzLCByZXN1bHRpbmcgaW4gdW5zdGFibGUgQVRFIGVzdGltYXRlcyB3aXRoIGhpZ2ggdmFyaWFuY2UgYW5kIHZhbHVlcyBvdXRzaWRlIHRoZSBjb25zdHJhaW50cyBvZiB0aGUgc3RhdGlzdGljYWwgbW9kZWwgW0BsdW5jZWZvcmQyMDA0XS4gIAoKRnVydGhlcm1vcmUsIHdoZW4gYW5hbHlpemluZyBvYnNlcnZhdGlvbmFsIGRhdGEgd2l0aCBhIGxhcmdlIG51bWJlciBvZiB2YXJpYWJsZXMgYW5kIHBvdGVudGlhbGx5IGNvbXBsZXggcmVsYXRpb25zaGlwcyBhbW9uZyB0aGVtLCBtb2RlbCBtaXNzcGVjaWZpY2F0aW9uIGR1cmluZyBlc3RpbWF0aW9uIGlzIG9mIHBhcnRpY3VsYXIgY29uY2Vybi4gSGVuY2UsIHRoZSBjb3JyZWN0IG1vZGVsIHNwZWNpZmljYXRpb24gaW4gcGFyYW1ldHJpYyBtb2RlbGxpbmcgaXMgY3J1Y2lhbCB0byBvYnRhaW4gdW5iaWFzZWQgZXN0aW1hdGVzIG9mIHRoZSB0cnVlIEFURSBbQHZhbjIwMTFdLiAKCkhvd2V2ZXIsIE1hcmsgdmFuIGRlciBMYWFuIGFuZCBSdWJpbiBbQHZhbjIwMDZdIGludHJvZHVjZWQgaW4gMjAwNiBhICoqZG91YmxlLXJvYnVzdCoqIGVzdGltYXRpb24gcHJvY2VkdXJlIHRvICoqcmVkdWNlIGJpYXMqKiBhZ2FpbnN0IG1pc3NwZWNpZmljYXRpb24uIFRoZSB0YXJnZXRlZCBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiAoKipUTUxFKiopIGlzIGEgc2VtaXBhcmFtZXRyaWMsIGVmZmljaWVudCBzdWJzdGl0dXRpb24gZXN0aW1hdG9yIFtAdmFuMjAxMV0uICAgICAgCgojVE1MRQoKKipUTUxFKiogYWxsb3dzIGZvciBkYXRhLWFkYXB0aXZlIGVzdGltYXRpb24gd2hpbGUgb2J0YWluaW5nIHZhbGlkIHN0YXRpc3RpY2FsIGluZmVyZW5jZSBiYXNlZCBvbiB0aGUgdGFyZ2V0ZWQgbWluaW11bSBsb3NzLWJhc2VkIGVzdGltYXRpb24gYW5kIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyB0byBtaW5pbWlzZSB0aGUgcmlzayBvZiBtb2RlbCBtaXNzcGVjaWZpY2F0aW9uIFtAdmFuMjAxMV0uIFRoZSBtYWluIGNoYXJhY3RlcmlzdGljcyBvZiAqKlRNTEUqKiBhcmU6ICAgICAgCgoxLiAqKlRNTEUqKiBpcyBhIGdlbmVyYWwgYWxnb3JpdGhtIGZvciB0aGUgY29uc3RydWN0aW9uIG9mIGRvdWJsZS1yb2J1c3QsIHNlbWlwYXJhbWV0cmljLCBlZmZpY2llbnQgc3Vic3RpdHV0aW9uIGVzdGltYXRvcnMuICoqVE1MRSoqIGFsbG93cyBmb3IgZGF0YS1hZGFwdGl2ZSBlc3RpbWF0aW9uIHdoaWxlIG9idGFpbmluZyB2YWxpZCBzdGF0aXN0aWNhbCBpbmZlcmVuY2UuIAoKMi4gKipUTUxFKiogaW1wbGVtZW50YXRpb24gdXNlcyB0aGUgRy1jb21wdXRhdGlvbiBlc3RpbWFuZCAoRy1mb3JtdWxhKS4gQnJpZWZseSwgdGhlICoqVE1MRSoqIGFsZ29yaXRobSB1c2VzIGluZm9ybWF0aW9uIGluIHRoZSBlc3RpbWF0ZWQgZXhwb3N1cmUgbWVjaGFuaXNtIFAoQXxXKSB0byB1cGRhdGUgdGhlIGluaXRpYWwgZXN0aW1hdG9yIG9mIHRoZSBjb25kaXRpb25hbCBleHBlY3RhdGlvbiBvZiB0aGUgb3V0Y29tZSBnaXZlbiB0aGUgdHJlYXRtZW50IGFuZCB0aGUgc2V0IG9mIGNvdmFyaWF0ZXMgVywgRSRfezB9JChZfEEsVykuIAoKMy4gVGhlIHRhcmdldGVkIGVzdGltYXRlcyBhcmUgdGhlbiBzdWJzdGl0dXRlZCBpbnRvIHRoZSBwYXJhbWV0ZXIgbWFwcGluZyAkXFBzaSQuIFRoZSB1cGRhdGluZyBzdGVwIGFjaGlldmVzIGEgdGFyZ2V0ZWQgYmlhcyByZWR1Y3Rpb24gZm9yIHRoZSBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QgJFxQc2koUF97MH0pJCAodGhlIHRydWUgdGFyZ2V0IHBhcmFtZXRlcikgYW5kIHNlcnZlcyB0byBzb2x2ZSB0aGUgZWZmaWNpZW50IHNjb3JlIGVxdWF0aW9uLCBuYW1lbHkgdGhlIEluZmx1ZW5jZSBDdXJ2ZSAoSUMpLiBBcyBhIHJlc3VsdCwgKipUTUxFKiogaXMgYSAqKmRvdWJsZS1yb2J1c3QqKiBlc3RpbWF0b3IuIAoKNC4gKipUTUxFKiogaXQgd2lsbCBiZSBjb25zaXN0ZW50IGZvciAkXFBzaShQX3swfSkkIGlmIGVpdGhlciB0aGUgY29uZGl0aW9uYWwgZXhwZWN0YXRpb24gRSRfezB9JChZfEEsVykgb3IgdGhlIGV4cG9zdXJlIG1lY2hhbmlzbSBQJF97MH0kKEF8VykgYXJlIGVzdGltYXRlZCBjb25zaXN0ZW50bHkuICAgCgo1LiAqKlRNTEUqKiB3aWxsIGJlIGVmZmljaWVudCBpZiB0aGUgcHJldmlvdXMgdHdvIGZ1bmN0aW9ucyBhcmUgY29uc2lzdGVudGx5IGVzdGltYXRlZCBhY2hpZXZpbmcgdGhlIGxvd2VzdCBhc3ltcHRvdGljIHZhcmlhbmNlIGFtb25nIGEgbGFyZ2UgY2xhc3Mgb2YgZXN0aW1hdG9ycy4gVGhlc2UgYXN5bXB0b3RpYyBwcm9wZXJ0aWVzIHR5cGljYWxseSB0cmFuc2xhdGUgaW50byAqKmxvd2VyIGJpYXMgYW5kIHZhcmlhbmNlKiogaW4gZmluaXRlIHNhbXBsZXMgW0BidWgyMDE2XS4gCgo2LiBUaGUgZ2VuZXJhbCBmb3JtdWxhIHRvIGVzdGltYXRlIHRoZSBBVEUgdXNpbmcgdGhlIFRNTEUgbWV0aG9kOiAgCgokJFxwc2kgVE1MRSxuID0gXFBzaShRX3tufV57Kn0pPSB7XGZyYWN7MX17bn1cc3VtX3tpPTF9XntufVxiYXJ7UX1fe259XnsxfVxsZWZ0KDEsXCBXX3tpfVxyaWdodCktXGJhcntRfV97bn1eezF9XGxlZnQoMCxcIFdfe2l9XHJpZ2h0KX0uICAoMSkkJAo3LiBUaGUgZWZmaWNpZW50IGluZmx1Y2VuY2UgY3VydmUgKElDKSBiYXNlZCBvbiBIYW1wZWwgc2VtaW5hbCBwYXBlciBbQGhhbXBlbDE5NzRdIGlzIGFwcGxpZWQgZm9yIHN0YXRpc3RpY2FsIGluZmVyZW5jZSB1c2luZyBUTUxFOiAgCgokJElDX3tufShPX3tpfSk9XGxlZnQoXGZyYWN7SVxsZWZ0KEFfe2l9PTFccmlnaHQpfXtnX25cbGVmdCgxXGxlZnR8V197aX1ccmlnaHQpXHJpZ2h0KX1cIC1cIFxmcmFje0lcbGVmdChBX3tpfT0wXHJpZ2h0KX17Z19uXGxlZnQoMFxsZWZ0fFdfe2l9XHJpZ2h0KVxyaWdodCl9XCBccmlnaHQpXGxlZnRbWV97aX0tXGJhcntRfV97bn1eezF9XGxlZnQoQV97aX0sV197aX1ccmlnaHQpXHJpZ2h0XStcYmFye1F9X3tufV57MX1cbGVmdCgxLFwgV197aX1ccmlnaHQpLVxiYXJ7UX1fe259XnsxfVxsZWZ0KDAsXCBXX3tpfVxyaWdodCkgLSBccHNpIFRNTEUsbi4gKDIpJCQgIAp3aGVyZSB0aGUgdmFyaWFuY2Ugb2YgdGhlIEFURTogIAoKJCRcc2lnbWEoe1xwc2lfezB9fSk9XHNxcnR7XGZyYWN7VmFyKElDX3tufSl9e259fS4gICgzKSQkICAKCjguIFRoZSBwcm9jZWR1cmUgaXMgYXZhaWxhYmxlIHdpdGggc3RhbmRhcmQgc29mdHdhcmUgc3VjaCBhcyB0aGUgKip0bWxlKiogcGFja2FnZSBpbiBSICBbQGdydWJlcjIwMTFdLiAKCiNTdHJ1Y3R1cmFsIGNhdXNhbCBmcmFtZXdvcmsKCiMjRGlyZWN0IEFjeWNsaWMgR3JhcGggKERBRykKIVtdKEZpZ3VyZXMvREFHLnBuZykKKipGaWd1cmUgMSoqLiBEaXJlY3QgQWN5Y2xpYyBHcmFwaCAgCioqU291cmNlKio6IE1pZ3VlbCBBbmdlbCBMdXF1ZS1GZXJuYW5kZXogIAoKIyNEQUcgaW50ZXJwcmV0YXRpb24gCgpUaGUgQVRFIGlzIGludGVycHJldGVkIGFzIHRoZSBwb3B1bGF0aW9uIHJpc2sgZGlmZmVyZW5jZSBpbiBvbmUteWVhciBtb3J0YWxpdHkgZm9yIGx1bmcgY2FuY2VyIHBhdGllbnRzIGRpYWdub3NlZCB2aWEgZW1lcmdlbmN5IHByZXNlbnRhdGlvbnMgdmVyc3VzIG5vbi1lbWVyZ2VuY3kgcHJlc2VudGF0aW9ucy4gVW5kZXIgY2F1c2FsIGFzc3VtcHRpb25zLCBhbmQgY29tcGFyZWQgd2l0aCBub24tZW1lcmdlbmN5IHByZXNlbnRhdGlvbnMgb2YgbHVuZyBjYW5jZXIsIHRoZSByaXNrIGRpZmZlcmVuY2Ugb2Ygb25lLXllYXIgbW9ydGFsaXR5IGZvciBlbWVyZ2VuY3kgcHJlc2VudGF0aW9ucyBpbmNyZWFzZXMgYnkgYXBwcm94aW1hdGVseSAqKjIwJSoqLiAKCiNDYXVzYWwgYXNzdW1wdGlvbnMgClRvIGVzdGltYXRlIHRoZSB2YWx1ZSBvZiB0aGUgdHJ1ZSBjYXVzYWwgdGFyZ2V0IHBhcmFtZXRlciAkXHBzaShQX3swfSkkIHdpdGggYSBtb2RlbCBmb3IgdGhlIHRydWUgZGF0YSBnZW5lcmF0aW9uIHByb2Nlc3MgJFBfezB9JCB1bmRlciB0aGUgY291bnRlcmZhY3R1YWwgZnJhbWV3b3JrIGF1Z21lbnRlZCBhZGRpdGlvbmFsIHVudGVzdGFibGUgY3Vhc2FsIGFzc3VtcHRpb25zIGhhdmUgdG8gYmUgY29uc2lkZXJlZCBbQHJ1YmluMjAxMV0sIFtAdmFuMjAxMV06ICAgICAgICAgIAoKIyNDTUkgb3IgUmFuZG9taXphdGlvbiAKKCRZX3swfSxZX3sxfVxwZXJwJEF8Vykgb3IgY29uZGl0aW9uYWwgbWVhbiBpbmRlcGVuZGVuY2UgKENNSSkgb2YgdGhlIGJpbmFyeSB0cmVhdG1lbnQgZWZmZWN0IChBKSBvbiB0aGUgb3V0Y29tZSAoWSkgZ2l2ZW4gdGhlIHNldCBvZiBvYnNlcnZlZCBjb3ZhcmlhdGVzIChXKSwgd2hlcmUgVyA9IChXMSwgIFcyLCBXMywg4oCmICwgJFx0ZXh0e1d9X3trfSQpLiAgCgojI1Bvc2l0aXZpdHkgICAKYSDPtSBBOiBQKEE9YSB8IFcpID4gMCAgIApQKEE9MXxXPXcpID4gMCBhbmQgUChBPTB8IFcgPSB3KSA+IDAgZm9yIGVhY2ggcG9zc2libGUgdy4gICAgICAKCiMjQ29uc2lzdGVuY3kgb3IgU1VUVkEgIApUaGUgU3RhYmxlIFVuaXQgVHJlYXRtZW50IFZhbHVlIEFzc3VtcHRpb24gKFNVVFZBKSBpbmNvcnBvcmF0ZXMKYm90aCB0aGlzIGlkZWEgdGhhdCAqKnVuaXRzIGRvIG5vdCBpbnRlcmZlcmUqKiB3aXRoIG9uZSBhbm90aGVyLCBhbmQgYWxzbyB0aGUgY29uY2VwdCB0aGF0IGZvciBlYWNoIHVuaXQgdGhlcmUgaXMgb25seSBhICoqc2luZ2xlIHZlcnNpb24gb2YgZWFjaCB0cmVhdG1lbnQgbGV2ZWwqKi4gICAgICAKCiNUTUxFIGZsb3cgY2hhcnQgCiFbXShGaWd1cmVzL3RtbGUucG5nKQoqKkZpZ3VyZSAyKiouIFRNTEUgZmxvdyBjaGFydCAoUm9hZCBtYXApICAKKipBZGFwdGVkIGZyb20qKjogTWFyayB2YW4gZGVyIExhYW4gYW5kIFNoZXJyaSBSb3NlLiBUYXJnZXRlZCBsZWFybmluZzogY2F1c2FsIGluZmVyZW5jZSBmb3Igb2JzZXJ2YXRpb25hbCBhbmQgZXhwZXJpbWVudGFsIGRhdGFTcHJpbmdlciBTZXJpZXMgaW4gU3RhdGlzdGljcywgMjAxMS4gIAoKI0RhdGEgZ2VuZXJhdGlvbgoKIyNTaW11bGF0aW9uIAoKSW4gUiB3ZSBjcmVhdGUgYSBmdW5jdGlvbiB0byBnZW5lcmF0ZSB0aGUgZGF0YS4gVGhlIGZ1bmN0aW9uIHdpbGwgaGF2ZSBhcyBpbnB1dCAqKm51bWJlciBvZiBkcmF3cyoqIGFuZCBhcyBvdXRwdXQgdGhlIGdlbmVyYXRlZCAqKm9ic2VydmVkIGRhdGEqKiAoT2JzRGF0YSkgaW5jbHVkaW5nIHRoZSBjb3VudGVyZmFjdHVhbHMgKFkxLCBZMCkuICAKClRoZSBzaW11bGF0ZWQgZGF0YSByZXBsaWNhdGlvbmcgdGhlIERBRyBpbiBGaWd1cmUgMTogIAoKMS4gWTogbW9ydGFsaXR5IGJpbmFyeSBpbmRpY2F0b3IgKDEgZGVhdGgsIDAgYWxpdmUpIAoyLiBBOiBiaW5hcnkgdHJlYXRtZW50IGZvciBlbWVyZ2VuY3kgcHJlc2VudGF0aW9uIGF0IGNhbmNlciBkaWFnbm9zaXMgICgxIEVQLCAwIE5vbkVQKSAgICAKMy4gVzE6IEdlbmRlciAoMSBtYWxlOyAwIGZlbWFsZSkgIAo0LiBXMjogQWdlIGF0IGRpYWdub3NpcyAoMCA8NjU7IDEgPj02NSkgIAo0LiBXMzogQ2FuY2VyIFROTSBjbGFzc2lmaWNhdGlvbiAoc2NhbGUgZnJvbSAxIHRvIDQpICAKNS4gVzQ6IENvbW9yYmlkaXRpZXMgKHNjYWxlIGZyb20gMSB0byA1KSAgCgpgYGB7cn0Kb3B0aW9ucyhkaWdpdHM9NCkKZ2VuZXJhdGVEYXRhIDwtIGZ1bmN0aW9uKG4pewogIHcxIDwtIHJiaW5vbShuLCBzaXplPTEsIHByb2I9MC41KQogIHcyIDwtIHJiaW5vbShuLCBzaXplPTEsIHByb2I9MC42NSkKICB3MyA8LSByb3VuZChydW5pZihuLCBtaW49MCwgbWF4PTQpLCBkaWdpdHM9MykKICB3NCA8LSByb3VuZChydW5pZihuLCBtaW49MCwgbWF4PTUpLCBkaWdpdHM9MykKICBBICA8LSByYmlub20obiwgc2l6ZT0xLCBwcm9iPSBwbG9naXMoLTAuNCArIDAuMip3MiArIDAuMTUqdzMgKyAwLjIqdzQgKyAwLjE1KncyKnc0KSkKICBZICA8LSByYmlub20obiwgc2l6ZT0xLCBwcm9iPSBwbG9naXMoLTEgKyBBIC0wLjEqdzEgKyAwLjMqdzIgKyAwLjI1KnczICsgMC4yKnc0ICsgMC4xNSp3Mip3NCkpCiAgCiAgIyBjb3VudGVyZmFjdHVhbAogIFkuMSA8LSByYmlub20obiwgc2l6ZT0xLCBwcm9iPSBwbG9naXMoLTEgKyAxIC0wLjEqdzEgKyAwLjMqdzIgKyAwLjI1KnczICsgMC4yKnc0ICsgMC4xNSp3Mip3NCkpCiAgWS4wIDwtIHJiaW5vbShuLCBzaXplPTEsIHByb2I9IHBsb2dpcygtMSArIDAgLTAuMSp3MSArIDAuMyp3MiArIDAuMjUqdzMgKyAwLjIqdzQgKyAwLjE1KncyKnc0KSkKICAKICAjIHJldHVybiBkYXRhLmZyYW1lCiAgZGF0YS5mcmFtZSh3MSwgdzIsIHczLCB3NCwgQSwgWSwgWS4xLCBZLjApCn0Kc2V0LnNlZWQoNzc3NykKT2JzRGF0YSA8LSBnZW5lcmF0ZURhdGEobj0xMDAwMCkKVHJ1ZV9Qc2kgPC0gbWVhbihPYnNEYXRhJFkuMS1PYnNEYXRhJFkuMCk7CmNhdCgiIFRydWVfUHNpOiIsIFRydWVfUHNpKQpCaWFzX1BzaSA8LSBsbShkYXRhPU9ic0RhdGEsIFl+IEEgKyB3MSArIHcyICsgdzMgKyB3NCkKY2F0KCJcbiIpCmNhdCgiXG4gTmFpdmVfQmlhc2VkX1BzaToiLHN1bW1hcnkoQmlhc19Qc2kpJGNvZWZbMiwgMV0pCk5haXZlX0JpYXMgPC0gKChzdW1tYXJ5KEJpYXNfUHNpKSRjb2VmWzIsIDFdKS1UcnVlX1BzaSk7IGNhdCgiXG4gTmFpdmVzIGJpYXM6IiwgTmFpdmVfQmlhcykKTmFpdmVfUmVsYXRpdmVfQmlhcyA8LSAoKChzdW1tYXJ5KEJpYXNfUHNpKSRjb2VmWzIsIDFdKS1UcnVlX1BzaSkvVHJ1ZV9Qc2kpKjEwMDsgY2F0KCJcbiBSZWxhdGl2ZSBOYWl2ZXMgYmlhczoiLCBOYWl2ZV9SZWxhdGl2ZV9CaWFzLCIlIikKYGBgCgojI0RhdGEgdmlzdWFsaXphdGlvbgoKYGBge3J9CiMgRFQgdGFibGUgPSBpbnRlcmFjdGl2ZQojIGluc3RhbGwucGFja2FnZXMoIkRUIikgIyBpbnN0YWxsIERUIGZpcnN0CmxpYnJhcnkoRFQpCmRhdGF0YWJsZShoZWFkKE9ic0RhdGEsIG4gPSBucm93KE9ic0RhdGEpKSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDUsIGRpZ2l0cyA9IDIpKQpgYGAKCiNUTUxFIHNpbXBsZSBpbXBsZW1lbnRhdGlvbgoKIyNTdGVwIDE6ICRRX3swfSQoQSwqKlcqKikKRXN0aW1hdGlvbiBvZiB0aGUgaW5pdGlhbCBwcm9iYWJpbGl0eSBvZiB0aGUgb3V0Y29tZSAoWSkgZ2l2ZW4gdGhlIHRyZWF0bWVudCAoQSkgYW5kIHRoZSBzZXQgb2YgY292YXJpYXRlcyAoVyksIGRlbm90ZWQgYXMgJFFfezB9JChBLCoqVyoqKS4gVG8gZXN0aW1hdGUgJFFfezB9JChBLCoqVyoqKSB3ZSBjYW4gdXNlIGEgc3RhbmRhcmQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbDogCgokJFx0ZXh0e2xvZ2l0fVtQKFk9MXxBLFcpXVwsPVwsXGJldGFfezB9XCwrXCxcYmV0YV97MX1BXCwrXCxcaGF0e1xiZXRhX3syfV57VH19Vy4kJCAKClRoZXJlZm9yZSwgd2UgY2FuIGVzdGltYXRlIHRoZSBpbml0aWFsIHByb2JhYmlsaXR5IGFzIGZvbGxvd3M6IAoKJCRcYmFye1F9XnswfShBLFcpXCw9XCxcdGV4dHtleHBpdH0oXGhhdHtcYmV0YV97MH19XCwrXCxcaGF0e1xiZXRhX3sxfX1BXCwrXCxcaGF0e1xiZXRhX3syfV57VH19VykuJCQgCgpUaGUgcHJlZGljdGVkIHByb2JhYmlsaXR5IGNhbiBiZSBlc3RpbWF0ZWQgdXNpbmcgdGhlIFN1cGVyLUxlYXJuZXIgbGlicmFyeSBpbXBsZW1lbnRlZCBpbiB0aGUgUiBwYWNrYWdlIOKAnFN1cGVyLUxlYXJuZXLigJ0gW0B2YW4yMDA3XSB0byBpbmNsdWRlIGFueSB0ZXJtcyB0aGF0IGFyZSBmdW5jdGlvbnMgb2YgQSBvciBXIChlLmcuLCBwb2x5bm9taWFsIHRlcm1zIG9mIEEgYW5kIFcsIGFzIHdlbGwgYXMgdGhlIGludGVyYWN0aW9uIHRlcm1zIG9mIEEgYW5kIFcsIGNhbiBiZSBjb25zaWRlcmVkKS4gICAKCkNvbnNlcXVlbnRseSwgZm9yIGVhY2ggc3ViamVjdCwgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGZvciBib3RoIHBvdGVudGlhbCBvdXRjb21lcyAkXGJhcntRfV57MH0oMCxXKSQgYW5kICAkXGJhcntRfV57MH0oMSxXKSQgY2FuIGJlIGVzdGltYXRlZCBieSBzZXR0aW5nIEEgPSAwIGFuZCBBID0gMSBmb3IgZXZlcnlvbmUgcmVzcGVjdGl2ZWx5OgokJFxiYXJ7UX1eezB9KDAsVylcLD1cLFx0ZXh0e2V4cGl0fShcaGF0e1xiZXRhX3swfX1cLCtcLFxoYXR7XGJldGFfezJ9XntUfX1XKSwkJAphbmQsICAKJCRcYmFye1F9XnswfSgxLFcpXCw9XCxcdGV4dHtleHBpdH0oXGhhdHtcYmV0YV97MH19XCwrXCxcaGF0e1xiZXRhX3sxfX1BXCwrXCxcaGF0e1xiZXRhX3syfV57VH19VykuJCQKKipOb3RlKio6IHNlZSBhcHBlbmRpeCBvbmUgZm9yIGEgc2hvcnQgaW50cm9kdWN0aW9uIHRvIHRoZSBTdXBlci1MZWFybmVyIGFuZCBlbnNlbWJsZSBsZWFybmluZyB0ZWNobmlxdWVzLiAKCmBgYHtyfQpPYnNEYXRhIDwtc3Vic2V0KE9ic0RhdGEsIHNlbGVjdD1jKHcxLHcyLHczLHc0LEEsWSkpClkgIDwtIE9ic0RhdGEkWQpBICA8LSBPYnNEYXRhJEEKdzEgPC0gT2JzRGF0YSR3MQp3MiA8LSBPYnNEYXRhJHcyCnczIDwtIE9ic0RhdGEkdzMKdzQgPC0gT2JzRGF0YSR3NAptICA8LSBnbG0oWSB+IEEgKyB3MSArIHcyICsgdzMgKyB3NCwgZmFtaWx5PWJpbm9taWFsLCBkYXRhPU9ic0RhdGEpClEgIDwtIGNiaW5kKFFBVyA9IHByZWRpY3QobSksCiAgICAgICAgICAgIFExVyA9IHByZWRpY3QobSwgbmV3ZGF0YT1kYXRhLmZyYW1lKEEgPSAxLCB3MSwgdzIsIHczLCB3NCkpLAogICAgICAgICAgICBRMFcgPSBwcmVkaWN0KG0sIG5ld2RhdGE9ZGF0YS5mcmFtZShBID0gMCwgdzEsIHcyLCB3MywgdzQpKSkKUTAgPC0gYXMuZGF0YS5mcmFtZShRKQpZMTwtUTAkUTFXIApZMDwtUTAkUTBXClFBMTwtZXhwKFkxKS8oMStleHAoWTEpKQpRQTA8LWV4cChZMCkvKDErZXhwKFkwKSkKI0ludmVyc2UgbG9naXQgKHByb2JhYmlsaXR5IHNjYWxlKQpwc2kgPC0gKGV4cChZMSkvKDErZXhwKFkxKSkgLSBleHAoWTApLygxK2V4cChZMCkpKQpQc2kgPC0gbWVhbihleHAoWTEpLygxK2V4cChZMSkpIC0gZXhwKFkwKS8oMStleHAoWTApKSk7IGNhdCgiXG4gUTA6IiwgUHNpKQpkZiA8LSByb3VuZChjYmluZChMb2dpdD0oUSksUHIuWTE9UUExLFByLlkwPVFBMCxQc2k9cHNpKSwgZGlnaXRzPSAzKQpgYGAKKipWaXN1YWxpemluZyoqIHRoZSBmaXJzdCBzdGVwOiAgCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KZGF0YXRhYmxlKGhlYWQoZGYsIG4gPSBucm93KGRmKSksIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSA1LCBkaWdpdHMgPSAzKSkKYGBgCiMjU3RlcCAyOiAkZ197MH0oQSxXKSQKRXN0aW1hdGlvbiBvZiB0aGUgcHJvYmFiaWxpdHkgb2YgdGhlIHRyZWF0bWVudCAoQSkgZ2l2ZW4gdGhlIHNldCBvZiBjb3ZhcmlhdGVzIChXKSwgZGVub3RlZCBhcyAkZ197MH0oQSxXKSQuIFdlIGNhbiB1c2UgYWdhaW4gYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGFuZCB0byBpbXByb3ZlIHRoZSBwcmVkaWN0aW9uIGFsZ29yaXRobSB3ZSBjYW4gdXNlIHRoZSBTdXBlci1MZWFybmVyIGxpYnJhcnkgb3IgYW55IG90aGVyIG1hY2hpbmUgbGVhcm5pbmcgc3RyYXRlZ3k6ICAKCiQkXHRleHR7bG9naXR9W1AoQT0xfFcpXVwsPVwsXGJldGFfezB9XCwrXCxcYmV0YV97MX1ee1R9Vy4kJCAKVGhlbiwgd2UgZXN0aW1hdGUgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBvZiBQKEF8VykgPSAkXGhhdHtnfSgxLFcpJCB1c2luZzogIAoKJCRcaGF0e2d9KDEsVylcLD1cLFx0ZXh0e2V4cGl0fVwsKFxoYXR7XGJldGFfezB9fVwsK1wsXGhhdHtcYmV0YV97MX1ee1R9fVcpLiQkIAoKYGBge3J9CmcgPC0gZ2xtKEEgfiB3MiArIHczICsgdzQsIGZhbWlseSA9IGJpbm9taWFsKQpnMVcgPSBwcmVkaWN0KGcsIHR5cGUgPSJyZXNwb25zZSIpO2NhdCgiXG4gUHJvcGVuc2l0eSBzY29yZSA9IGcxVyIsIlxuIik7c3VtbWFyeShnMVcpCmBgYAoKIyNTdGVwIDM6IEhBVyBhbmQgJFxlcHNpbG9uJApUaGlzIHN0ZXAgYWltcyB0byBmaW5kIGEgYmV0dGVyIHByZWRpY3Rpb24gbW9kZWwgdGFyZ2V0ZWQgYXQgbWluaW1pc2luZyB0aGUgbWVhbiBzcXVhcmVkIGVycm9yIChNU0UpIGZvciB0aGUgcG90ZW50aWFsIG91dGNvbWVzLiBGb3IgdGhlIEFURSBvbiBzdGVwIGNvbnZlcmdlbmNlIGlzIGd1YXJhbnRlZWQgZ2l2ZW4gJFxiYXJ7UX1eezB9JCBhbmQgJFxoYXR7Z30oMSxXKSQuICAKClRoZSBmbHVjdHVhdGlvbiBwYXJhbWV0ZXJzICQoXGhhdHtcZXBzaWxvbn1fezB9XCwsXCxcaGF0e1xlcHNpbG9ufV97MX0pJCBhcmUgZXN0aW1hdGVkIHVzaW5nIG1heGltdW0gbGlrZWxpaG9vZCBwcm9jZWR1cmVzIGJ5IHNldHRpbmcgJFx0ZXh0e2xvZ2l0fShcYmFye1FeezB9fShBLFcpKSQgYXMgYW4gb2Zmc2V0IGluIGEgaW50ZXJjZXB0LWZyZWUgbG9naXN0aWMgcmVncmVzc2lvbiB3aXRoICRIX3swfSQgYW5kICRIX3sxfSQgYXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzOiAgICAgIAoKJCRcYmFye1FeezF9fShBLFcpXCw9XCxcdGV4dHtleHBpdH1cbGVmdFtcdGV4dHtsb2dpdH1cbGVmdChcYmFye1FeezB9fShBLCBXKVxyaWdodClcLCtcLFxoYXR7XGVwc2lsb25fezB9fUhfezB9KEEsVylcLCtcLFxoYXR7XGVwc2lsb25fezF9fUhfezF9KEEsVylccmlnaHRdICAoNSkkJAokJFxiYXJ7UV57MX19KDAsVylcLD1cLFx0ZXh0e2V4cGl0fVxsZWZ0W1x0ZXh0e2xvZ2l0fVxsZWZ0KFxiYXJ7UV57MH19KEEsVylccmlnaHQpXCwrXCxcaGF0e1xlcHNpbG9uX3swfX1IX3swfSgwLFcpXHJpZ2h0XSQkCgokJFxiYXJ7UV57MX19KDEsVylcLD1cLFx0ZXh0e2V4cGl0fVxsZWZ0W1x0ZXh0e2xvZ2l0fVxsZWZ0KFxiYXJ7UV57MH19KEEsVylccmlnaHQpXCwrXCxcaGF0e1xlcHNpbG9uX3sxfX1IX3sxfSgxLFcpXHJpZ2h0XSQkCldoZXJlLAokJEhfezB9KEEsVylcLD1cLFxmcmFje0koQT0wKX17XGhhdHtnfSgwfFcpfVw7XHRleHR7YW5kfSxcO0hfezF9KEEsVylcLD1cLFxmcmFje0koQT0xKX17XGhhdHtnfSgxfFcpfSQkIGFyZSB0aGUgc3RhYmlsaXplZCBpbnZlcnNlIHByb2JhYmlsaXR5IG9mIHRyZWF0bWVudCAoQSkgd2VpZ2h0cyAoSVBUVyksIG5hbWVsbHkgdGhlICoqY2xldmVyIGNvdmFyaWF0ZXMqKiBhbmQgKipJKiogZGVmaW5lcyBhbiBpbmRpY2F0b3IgZnVuY3Rpb24gKG5vdGUgdGhhdCAkXGhhdHtnfShBfFcpJCBpcyBlc3RpbXRlZCBmcm9tIHN0ZXAgMikuICAKCmBgYHtyfQojQ2xldmVyIGNvdmFyaWF0ZSBhbmQgZmx1Y3R1YXRpbmcvc3Vic3RpdHV0aW9uIHBhcmFtdGVyZXMKaCA8LSBjYmluZChnQVc9KEEvZzFXIC0oMS1BKS8oMS1nMVcpKSwgZzFXPSgxL2cxVyksIGcwVz0oLTEvKDEtZzFXKSkpCmVwc2lsb24gPC0gY29lZihnbG0oWSB+IC0xICsgaFssMV0gKyBvZmZzZXQoUVssIlFBVyJdKSwgZmFtaWx5ID0gYmlub21pYWwpKTtjYXQoIlxuIEVwc2lsb246IixlcHNpbG9uKQpkZiA8LSByb3VuZChjYmluZChRMCxQUz0oZzFXKSxIPShoKSxlcHNpbG9uKSwgZGlnaXRzPSA0KQpgYGAKKipWaXN1YWxpemluZyoqIHRoZSAzcmQgc3RlcCAoUFMgPSBwcm9wZW5zaXR5IHNjb3JlOyBIID0gSVBUVyBvciBjbGV2ZXIgY292YXJpdGVzKTogICAgCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KZGF0YXRhYmxlKGhlYWQoZGYsIG4gPSBucm93KGRmKSksIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSA1LCBkaWdpdHMgPSAzKSkKYGBgCgojI1N0ZXAgNCAkXGJhcntRX3tufX1eeyp9Olx0ZXh0e2Zyb219XCxcYmFye1FfezB9fV57MH1cLFx0ZXh0e3RvfVwsXGJhcntRX3sxfX1eezF9JAoKQWZ0ZXJ3YXJkcywgdGhlIGVzdGltYXRlZCBwcm9iYWJpbGl0eSBvZiB0aGUgcG90ZW50aWFsIG91dGNvbWVzIGlzIHVwZGF0ZWQgYnkgdGhlIHN1YnN0aXR1dGlvbiBwYXJhbWV0ZXJzICQoXGhhdHtcZXBzaWxvbl97MH19XCwsXCxcaGF0e1xlcHNpbG9uX3sxfX0pJC4gVGhlIHN1YnN0aXR1dGlvbiB1cGRhdGUgaXMgcGVyZm9ybWVkIGJ5IHNldHRpbmcgQSA9IDAgYW5kIEEgPSAxIGZvciBlYWNoIHN1YmplY3QgaW4gdGhlIGluaXRpYWwgZXN0aW1hdGUgcHJvYmFiaWxpdHkgb2YgdGhlIHBvdGVudGlhbCBvdXRjb21lcyAkXGJhcntRXnswfX0oMCxXKVwsLFwsXGJhcntRXnswfX0oMSxXKSQsIGFzIHdlbGwgYXMgaW4gdGhlIGNsZXZlciBjb3ZhcmlhdGVzICRIX3swfSgwLFcpXDtcdGV4dHthbmR9XDsgSF97MX0oMSxXKSQuIAoKRm9yIHRoZSAkXFBzaShcYmFye1Ffe259fV57Kn0pJCwgdGhlIHVwZGF0ZWQgZXN0aW1hdGUgb2YgdGhlIHBvdGVudGlhbCBvdXRjb21lcyBvbmx5IG5lZWRzIG9uZSBpdGVyYXRpb24gJFxQc2koXGJhcntRX3tufX1eeyp9KSQgZnJvbSAkXGJhcntRfV57MH0oQSxXKVwsPT5cYmFye1FeezF9fShBLFcpJC4gVGhlcmVmb3JlLCBtb2RlbCAoNSkgdGFyZ2V0cyAkRVtcaGF0e1l9X3tBPTB9XVw7XHRleHR7YW5kfVw7IEVbXGhhdHtZfV97QT0xfV0kIHNpbXVsdGFuZW91c2x5IGJ5IGluY2x1ZGluZyBib3RoICRIX3swfShBLFcpXCxcdGV4dHthbmR9XCxIX3sxfShBLFcpJCBpbiB0aGUgbW9kZWwuIEhlbmNlICRccHNpJCBpcyBmaW5hbGx5IGVzdGltYXRlZCBhcyBmb2xsb3dzOiAgCgokJFxwc2kgVE1MRSxuID0gXFBzaShRX3tufV57Kn0pPSB7XGZyYWN7MX17bn1cc3VtX3tpPTF9XntufVxiYXJ7UX1fe259XnsxfVxsZWZ0KDEsXCBXX3tpfVxyaWdodCktXGJhcntRfV97bn1eezF9XGxlZnQoMCxcIFdfe2l9XHJpZ2h0KX0uICAoMSkkJAoKYGBge3J9ClFzdGFyIDwtIHBsb2dpcyhRICsgZXBzaWxvbipoKQpwc2kgPC0gKFFzdGFyWywiUTFXIl0gLSBRc3RhclssIlEwVyJdKQpQc2kgPC0gbWVhbihRc3RhclssIlExVyJdIC0gUXN0YXJbLCJRMFciXSk7CmNhdCgiVE1MRV9Qc2k6IiwgUHNpKQpjYXQoIlxuIFRNTEUuU0lfYmlhczoiLCBhYnMoVHJ1ZV9Qc2ktUHNpKSkKY2F0KCJcbiBSZWxhdGl2ZV9UTUxFLlNJX2JpYXM6IixhYnMoVHJ1ZV9Qc2ktUHNpKS9UcnVlX1BzaSoxMDAsIiUiKQpgYGAKCioqVmlzdWFsaXppbmcqKiB0aGUgNHRoIHN0ZXAgKEggPSBJUFRXIG9yIGNsZXZlciBjb3Zhcml0ZXMpOiAgCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KZGYgPC0gcm91bmQoY2JpbmQoUTA9KFEwKSxIPShoKSxlcHNpbG9uLHBzaSksIGRpZ2l0cz0gNCkKZGF0YXRhYmxlKGhlYWQoZGYsIG4gPSBucm93KGRmKSksIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSA1LCBkaWdpdHMgPSAzKSkKYGBgCmBgYHtyfQpjYXQoIlxuIFBzaSBmaXJzdCByb3c6IiwgcGxvZ2lzKCgwLjAwMTE4OSoxLjIzOTkpKygyLjM2NzgpKS0ocGxvZ2lzKCgwLjAwMTE4OSotNS4xNjgxKSsoMS4zNDEzKSkpKQpjYXQoIlxuIFRNTEVfUHNpOiIsIFBzaSkKYGBgCgojI1N0ZXAgNTogSW5mZXJlbmNlClRNTEUgdXNlcyB0aGUgZWZmaWNpZW50IGluZmx1ZW5jZSBjdXJ2ZSAoSUMpIGZvciBpbmZlcmVuY2UgKGkuZS4sIHRvIG9idGFpbiBzdGFuZGFyZCBlcnJvcnMgZm9yICRccHNpJCkuICAgCgokJElDX3tufShPX3tpfSlcIFwgPVwgXGxlZnQoXGZyYWN7SVxsZWZ0KEFfe2l9PTFccmlnaHQpfXtnX25cbGVmdCgxXGxlZnR8V197aX1ccmlnaHQpXHJpZ2h0KX1cIC1cIFxmcmFje0lcbGVmdChBX3tpfT0wXHJpZ2h0KX17Z19uXGxlZnQoMFxsZWZ0fFdfe2l9XHJpZ2h0KVxyaWdodCl9XCBccmlnaHQpXGxlZnRbWV97aX0tXGJhcntRfV97bn1eezF9XGxlZnQoQV97aX0sV197aX1ccmlnaHQpXHJpZ2h0XStcYmFye1F9X3tufV57MX1cbGVmdCgxLFwgV197aX1ccmlnaHQpLVxiYXJ7UX1fe259XnsxfVxsZWZ0KDAsXCBXX3tpfVxyaWdodCkgLSBccHNpIFRNTEUsbi4gKDIpJCQKCndoZXJlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gZm9yICRccHNpJCBpcyBlc3RpbWF0ZWQgYXMgZm9sbG93czogIAoKJCRcc2lnbWEoe1xwc2lfezB9fSk9XHNxcnR7XGZyYWN7VmFyKElDX3tufSl9e259fS4gICgzKSQkCioqTm90ZSoqOiBzZWUgYXBwZW5kaXggdHdvIGZvciBhIHNob3J0IGludHJvZHVjdGlvbiB0byB0aGUgdGhlb3J5IG9mIHRoZSBJbmZsdWVuY2UgQ3VydmUuICAgCgpgYGB7cn0KUSAgPC0gYXMuZGF0YS5mcmFtZShRKQpJQyA8LSBoWywxXSooWS1RJFFBVykgKyBRJFExVyAtIFEkUTBXIC0gUHNpO3N1bW1hcnkoSUMpCm4gPC0gbnJvdyhPYnNEYXRhKQp2YXJIYXQuSUMgPC0gdmFyKElDKS9uOyB2YXJIYXQuSUMKCiNQc2kgYW5kIDk1JUNJIGZvciBQc2kKY2F0KCJcbiBUTUxFX1BzaToiLCBQc2kpCmNhdCgiXG4gOTUlQ0k6IiwgYyhQc2ktMS45NipzcXJ0KHZhckhhdC5JQyksICBQc2krMS45NipzcXJ0KHZhckhhdC5JQykpKQoKY2F0KCJcbiBUTUxFLlNJX2JpYXM6IiwgYWJzKFRydWVfUHNpLVBzaSkpCmNhdCgiXG4gUmVsYXRpdmVfVE1MRS5TSV9iaWFzOiIsYWJzKFRydWVfUHNpLVBzaSkvVHJ1ZV9Qc2kqMTAwLCIlIikKYGBgCgojVE1MRSB2cy4gQUlQVFcKMS4gVGhlIGFkdmFudGFnZXMgb2YgKipUTUxFKiogaGF2ZSByZXBlYXRlZGx5IGJlZW4gZGVtb25zdHJhdGVkIGluIGJvdGggc2ltdWxhdGlvbiBzdHVkaWVzIGFuZCBhcHBsaWVkIGFuYWx5c2VzIFtAdmFuMjAxMV0uIAoKMi4gRXZpZGVuY2Ugc2hvd3MgdGhhdCAqKlRNTEUqKiBwcm92aWRlcyB0aGUgbGVzcyB1bmJpYXNlZCBBVEUgZXN0aW1hdGUgY29tcGFyZWQgd2l0aCBvdGhlciBkb3VibGUtcm9idXN0IGVzdGltYXRvcnMgW0BuZXUyMDA1XSwgW0B2YW4yMDExXSBzdWNoIGFzIHRoZSBjb21iaW5hdGlvbiBvZiByZWdyZXNzaW9uIGFkanVzdG1lbnQgd2l0aCBpbnZlcnNlIHByb2JhYmlsaXR5IG9mIHRyZWF0bWVudCB3ZWlnaHRpbmcgKElQVFctUkEpIGFuZCB0aGUgYXVnbWVudGVkIGludmVyc2UgcHJvYmFiaWxpdHkgb2YgdHJlYXRtZW50IHdlaWdodGluZyAoQUlQVFcpLiBUaGUgKipBSVBUVyoqIGVzdGltYXRpb24gaXMgYSB0d28tc3RlcCBwcm9jZWR1cmUgd2l0aCB0d28gZXF1YXRpb25zIChwcm9wZW5zaXR5IHNjb3JlIGFuZCBtZWFuIG91dGNvbWUgZXF1YXRpb25zKS4gIAoKMy4gVG8gZXN0aW1hdGUgdGhlIEFURSB1c2luZyB0aGUgKipBSVBUVyoqIGVzdGltYXRvciBvbmUgY2FuIHNldCB0aGUgZXN0aW1hdGlvbiBlcXVhdGlvbiAoRUUpICg0KSBlcXVhbCB0byB6ZXJvIGFuZCB1c2UgYm9vdHN0cmFwIHRvIGRlcml2ZSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbHMgKENJKS4gSG93ZXZlciwgc29sdmluZyB0aGUgRUUgdXNpbmcgdGhlIGdlbmVyYWxpemVkIG1ldGhvZCBvZiBtb21lbnRzIChHTU0pLCBzdGFja2luZyBib3RoIGVxdWF0aW9ucyAocHJvcGVuc2l0eSBzY29yZSBhbmQgb3V0Y29tZSksIHJlZHVjZXMgdGhlIGVzdGltYXRpb24gYW5kIGluZmVyZW5jZSBzdGVwcyB0byBvbmx5IG9uZS4gSG93ZXZlciwgZ2l2ZW4gdGhhdCB0aGUgcHJvcGVuc2l0eSBzY29yZSBpbiBlcXVhdGlvbiAoNCkgY2FuIGVhc2lseSBmYWxsIG91dHNpZGUgdGhlIHJhbmdlIFswLCAxXSAoaWYgZm9yIHNvbWUgb2JzZXJ2YXRpb25zICRnX3tufSgxfFdfe2l9KSQgaXMgY2xvc2UgdG8gMSBvciAwKSB0aGUgKipBSVBUVyoqIGVzdGltYXRpb24gY2FuIGJlIHVuc3RhYmxlIChuZWFyIHZpb2xhdGlvbiBvZiB0aGUgcG9zaXRpdml0eSBhc3N1bXB0aW9uKS4gKipBSVBUVyoqIGluc3RhYmlsaXR5IHVuZGVyIG5lYXIgdmlvbGF0aW9uIG9mIHRoZSBwb3NpdGl2aXR5IGFzc3VtcHRpb24gcmVwcmVzZW50cyB0aGUgcHJpY2Ugb2Ygbm90IGJlaW5nIGEgc3Vic3RpdHV0aW9uIGVzdGltYXRvciBhcyAqKlRNTEUqKi4gICAgICAgIAoKJCRccHNpX3swfV57QUlQVFctQVRFfVwgXCA9XCBcZnJhY3sxfXtufVxzdW1fe2k9MX1ee259XGxlZnQoXGZyYWN7SVxsZWZ0KEFfe2l9PTFccmlnaHQpfXtnX25cbGVmdCgxXGxlZnR8V197aX1ccmlnaHQpXHJpZ2h0KX1cIC1cIFxmcmFje0lcbGVmdChBX3tpfT0wXHJpZ2h0KX17Z19uXGxlZnQoMFxsZWZ0fFdfe2l9XHJpZ2h0KVxyaWdodCl9XCBccmlnaHQpXGxlZnRbWV97aX0tXGJhcntRfV97bn1eezB9XGxlZnQoQV97aX0sV197aX1ccmlnaHQpXHJpZ2h0XStcZnJhY3sxfXtufVxzdW1fe2k9MX1ee259XGJhcntRfV97bn1eezB9XGxlZnQoMSxcIFdfe2l9XHJpZ2h0KS1cYmFye1F9X3tufV57MH1cbGVmdCgwLFwgV197aX1ccmlnaHQpLiAoNCkkJAoKYGBge3J9CkFJUFRXIDwtIG1lYW4oKGhbLDFdKihZLVEkUUFXKSkrKFEkUTFXLVEkUTBXKSk7IEFJUFRXCmNhdCgiXG4gQUlQVFdfYmlhczoiLCBhYnMoVHJ1ZV9Qc2ktQUlQVFcpKQpjYXQoIlxuIFJlbGF0aXZlX0FJUFRXX2JpYXM6IixhYnMoVHJ1ZV9Qc2ktQUlQVFcpL1RydWVfUHNpKjEwMCwiJSIpCmBgYAoKQ29tcGFyZWQgd2l0aCBBSVBUVywgVE1MRSBzaG93ZWQgc21hbGxlciByZWxhdGl2ZSBiaWFzLiAgCgojVE1MRSB1c2luZyB0aGUgU3VwZXItTGVhcm5lciAKCldpdGggVE1MRSB3ZSBjYW4gY2FsbCB0aGUgU3VwZXItTGVhcm5lciAoU0wpLiBUaGUgU0wgaXMgYSBSLXBhY2thZ2UgdXNpbmcgVi1mb2xkIGNyb3NzLXZhbGlkYXRpb24gYW5kIGVuc2VtYmxlZCBsZWFybmluZyAocHJlZGljdGlvbiB1c2luZyBhbGwgdGhlIHByZWRpY3Rpb25zIG9mIG11bHRpcGxlIHN0YWNrZWQgbGVhcm5pbmcgYWxnb3JpdGhtcykgdGVjaG5pcXVlcyB0byBpbXByb3ZlIG1vZGVsIHByZWRpY3Rpb24gcGVyZm9ybWFuY2UgW0BicmVpbWFuMTk5Nl0uICAKClRoZSBiYXNpYyBpbXBsZW1lbnRhdGlvbiBvZiBUTUxFIGluIHRoZSBSLXBhY2thZ2UgKip0bWxlKiogdXNlcyBieSBkZWZhdWx0IHRocmVlIGFsZ29yaXRobXM6ICAgIAoxLiBTTC5nbG0gKG1haW4gdGVybXMgbG9naXN0aWMgcmVncmVzc2lvbiBvZiBBIGFuZCBXKSwgICAgCjIuIFNMLnN0ZXAgKHN0ZXB3aXNlIGZvcndhcmQgYW5kIGJhY2t3YXJkIG1vZGVsIHNlbGVjdGlvbiB1c2luZyBBSUMgY3JpdGVyaW9uLCByZXN0cmljdGVkIHRvIHNlY29uZCBvcmRlciBwb2x5bm9taWFscykgYW5kLCAgCjMuIFNMLmdsbS5pbnRlcmFjdGlvbiAoYSBnbG0gdmFyaWFudCB0aGF0IGluY2x1ZGVzIHNlY29uZCBvcmRlciBwb2x5bm9taWFscyBhbmQgdHdvIGJ5IHR3byBpbnRlcmFjdGlvbnMgb2YgdGhlIG1haW4gdGVybXMgaW5jbHVkZWQgaW4gdGhlIG1vZGVsKS4gICAgCgpUaGUgcHJpbmNpcGFsIGludGVyZXN0IG9mIGNhbGxpbmcgdGhlIFN1cGVyLUxlYXJuZXIgaXMgdG8gb2J0YWluIHRoZSBsZXNzLXVuYmlhc2VkIGVzdGltYXRlZCBmb3IgJFxiYXIgUV97bn1eezB9KEEsVykkIGFuZCAkZ197MH0oQSxXKSQuIEl0IGlzIGFjaGlldmVkIGJ5IG9idGFpbmluZyB0aGUgc21hbGxlc3QgZXhwZWN0ZWQgbG9zcyBmdW5jdGlvbiBmb3IgWSBvciBBIChiaW5hcnkgb3V0Y29tZXMpLCByZXNwZWN0aXZlbHkuIEZvciBpbnN0YW5jZSwgdGhlIG5lZ2F0aXZlIGxvZ2FyaXRobWljIGxvc3MgZnVuY3Rpb24gZm9yIFkgaXMgY29tcHV0ZWQgYXMgdGhlIG1pbmltaXplciBvZiB0aGUgZXhwZWN0ZWQgc3F1YXJlZCBlcnJvciBsb3NzOiAgIAokJFxiYXIgUV97MH1cLD1cLCBcdGV4dHthcmcgbWlufV97XGJhciBRfUVfezB9TChPLCBcYmFyIFEpLCQkICAgCndoZXJlICRMKE8sIFxiYXIgUSkkIGlzOgokJCAoWSBcLC1cLCBcYmFyIFEoQSwgVykpXnsyfSQkCioqTm90ZSoqOiBzZWUgYXBwZW5kaXggb25lIGZvciBhIHNob3J0IGludHJvZHVjdGlvbiB0byB0aGUgU3VwZXItTGVhcm5lciBhbmQgZW5zZW1ibGUgbGVhcm5pbmcgdGVjaG5pcXVlcy4gCgoxLiAqKlN0ZXAgT25lKio6ICRcYmFyIFFfe259XnswfShBLFcpJCBwcmVkaWN0aW9uCgpgYGB7cn0KI0UoWXxBLFcpIHByZWRpY3Rpb24KbGlicmFyeShTdXBlckxlYXJuZXIpCiNTcGVjaWZ5IFN1cGVyTGVhcm5lciBsaWJyYXJpZXMKU0wubGlicmFyeSA8LSBjKCJTTC5nbG0iLCJTTC5zdGVwIiwiU0wuZ2xtLmludGVyYWN0aW9uIikKI0RhdGEgZnJhbWUgd2l0aCBYIHdpdGggYmFzZWxpbmUgY292YXJpYXRlcyBhbmQgZXhwb3N1cmUgQQpYIDwtIHN1YnNldChPYnNEYXRhLCBzZWxlY3Q9YyhBLCB3MSwgdzIsIHczLCB3NCkpCm4gPC0gbnJvdyhPYnNEYXRhKQojQ3JlYXRlIGRhdGEgZnJhbWVzIHdpdGggQT0xIGFuZCBBPTAKWDE8LVgwPC1YClgxJEEgPC0xClgwJEEgPC0wCiNDcmVhdGUgbmV3IGRhdGEgYnkgc3RhY2tpbmcgWCwgWDEsIGFuZCBYMApuZXdkYXRhIDwtIHJiaW5kKFgsWDEsWDApCiNDYWxsIHN1cGVybGVhcm5lcgpRaW5pdCA8LSBTdXBlckxlYXJuZXIoWT1PYnNEYXRhJFksIFg9WCwgbmV3WD1uZXdkYXRhLCBTTC5saWJyYXJ5PVNMLmxpYnJhcnksIGZhbWlseT0iYmlub21pYWwiKQpRaW5pdAojUHJlZGljdGlvbnMKI1ByZWQgcHJvYiBvZiBtb3J0YWxpdHkgKFkpIGdpdmVuIEEsIFcKUWJhckFXIDwtIFFpbml0JFNMLnByZWRpY3RbMTpuXQojUHJlZCBwcm9iIG9mIGR5aW5nIGZvciBlYWNoIHN1YmplY3QgZ2l2ZW4gQT0xIGFuZCB3ClFiYXIxVyA8LSBRaW5pdCRTTC5wcmVkaWN0WyhuKzEpOigyKm4pXQojUHJlZCBwcm9iIG9mIGR5aW5nIGZvciBlYWNoIHN1YmplY3QgZ2l2ZW4gQT0wIGFuZCB3ClFiYXIwVyA8LSBRaW5pdCRTTC5wcmVkaWN0WygyKm4rMSk6KDMqbildCiNTaW1wbGUgc3Vic3RpdHV0aW9uIGVzdGltYXRvciBQc2koUTApClBzaUhhdC5TUyA8LSBtZWFuKFFiYXIxVy1RYmFyMFcpO1BzaUhhdC5TUwpgYGAKCjIuICoqU3RlcCB0d28qKjogJGdfezB9KEEsVykkIHByZWRpY3Rpb24KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojU3RlcCAyIGdfMChBfFcpIHdpdGggU3VwZXJMZWFybmVyCncgPC0gc3Vic2V0KE9ic0RhdGEsIHNlbGVjdD1jKHcxLHcyLHczLHc0KSkKZ0hhdFNMIDwtIFN1cGVyTGVhcm5lcihZPU9ic0RhdGEkQSwgWD13LCBTTC5saWJyYXJ5PVNMLmxpYnJhcnksIGZhbWlseSA9IGJpbm9taWFsKQpnSGF0U0w7bWVhbihnSGF0U0wpCgojR2VuZXJhdGUgdGhlIHByZWQgcHJvYiBvZiBBPTEgYW5kLCBBPTAgZ2l2ZW4gY292YXJpYXRlcwpnSGF0MVcgPC0gZ0hhdFNMJFNMLnByZWRpY3QKZ0hhdDBXIDwtIDEtZ0hhdDFXCgojU3RlcCAzOiBDbGV2ZXIgY292YXJpYXRlCkhBVyA8LSBhcy5udW1lcmljKE9ic0RhdGEkQT09MSkvZ0hhdDFXIC0gYXMubnVtZXJpYyhPYnNEYXRhJEE9PTApL2dIYXQwVzttZWFuKEhBVykKSDFXIDwtICAxL2dIYXQxVwpIMFcgPC0gLTEvZ0hhdDBXCmBgYAozLiAqKlN0ZXBzIDMgYW5kIDQqKjogZmx1Y3R1YXRpb24gc3RlcCBhbmQgc3Vic3RpdHV0aW9uIGVzdGltYXRpb24gZm9yICRcYmFyIFFfe259XnswfShBLFcpJCB0byAkXGJhciBRX3tufV57MX0oQSxXKSQgIAoKYGBge3J9CiNTdGVwIDQ6IFN1YnN0aXR1dGlvbiBlc3RpbWFpdG9uIFEqIG9mIHRoZSBBVEUuCmxvZ2l0VXBkYXRlIDwtIGdsbShPYnNEYXRhJFkgfiAtMSArIG9mZnNldChxbG9naXMoUWJhckFXKSkrSEFXLCBmYW1pbHk9J2Jpbm9taWFsJykKZXBzIDwtIGxvZ2l0VXBkYXRlJGNvZWY7ZXBzCiNDYWxjdWxhdGluZyB0aGUgcHJlZGljdGVkIHZhbHVlcyBmb3IgZWFjaCBzdWJqZWN0IHVuZGVyIGVhY2ggdHJlYXRtZW50IEE9MSwgQT0wClFiYXJBVy5zdGFyIDwtIHBsb2dpcyhxbG9naXMoUWJhckFXKStlcHMqSEFXKQpRYmFyMVcuc3RhciA8LSBwbG9naXMocWxvZ2lzKFFiYXIxVykrZXBzKkgxVykKUWJhcjBXLnN0YXIgPC0gcGxvZ2lzKHFsb2dpcyhRYmFyMFcpK2VwcypIMFcpClBzaUhhdC5UTUxFLlNMIDwtIG1lYW4oUWJhcjFXLnN0YXIpIC0gbWVhbihRYmFyMFcuc3RhcikKY2F0KCJQc2lIYXQuVE1MRS5TTDoiLCBQc2lIYXQuVE1MRS5TTCkKY2F0KCJcbiBQc2lIYXQuVE1MRS5TTF9iaWFzOiIsIGFicyhUcnVlX1BzaS1Qc2lIYXQuVE1MRS5TTCkpCmNhdCgiXG4gUmVsYXRpdmVfUHNpSGF0LlRNTEUuU0xfYmlhczoiLGFicyhUcnVlX1BzaS1Qc2lIYXQuVE1MRS5TTCkvVHJ1ZV9Qc2kqMTAwLCIlIikKYGBgCgojUi1UTUxFCgpVc2luZyB0aGUgUi1wYWNrYWdlICoqdG1sZSoqLiAgCgpUaGUgYmFzaWMgaW1wbGVtZW50YXRpb24gb2YgVE1MRSBpbiB0aGUgUi1wYWNrYWdlICoqdG1sZSoqIHVzZXMgYnkgZGVmYXVsdCB0aHJlZSBhbGdvcml0aG1zOiAgICAgIAoxLiBTTC5nbG0gKG1haW4gdGVybXMgbG9naXN0aWMgcmVncmVzc2lvbiBvZiBBIGFuZCBXKSwgICAgCjIuIFNMLnN0ZXAgKHN0ZXB3aXNlIGZvcndhcmQgYW5kIGJhY2t3YXJkIG1vZGVsIHNlbGVjdGlvbiB1c2luZyBBSUMgY3JpdGVyaW9uLCByZXN0cmljdGVkIHRvIHNlY29uZCBvcmRlciBwb2x5bm9taWFscykgYW5kLCAgCjMuIFNMLmdsbS5pbnRlcmFjdGlvbiAoYSBnbG0gdmFyaWFudCB0aGF0IGluY2x1ZGVzIHNlY29uZCBvcmRlciBwb2x5bm9taWFscyBhbmQgdHdvIGJ5IHR3byBpbnRlcmFjdGlvbnMgb2YgdGhlIG1haW4gdGVybXMgaW5jbHVkZWQgaW4gdGhlIG1vZGVsKS4gICAgCgpgYGB7cn0KbGlicmFyeSh0bWxlKQp3IDwtIHN1YnNldChPYnNEYXRhLCBzZWxlY3Q9Yyh3MSx3Mix3Myx3NCkpCnRtbGUgPC0gdG1sZShZLCBBLCBXPXcpCmNhdCgiVE1MRVJfUHNpOiIsIHRtbGUkZXN0aW1hdGVzW1syXV1bWzFdXSwiOyIsIjk1JUNJKCIsIHRtbGUkZXN0aW1hdGVzW1syXV1bWzNdXSwiKSIpCmNhdCgiXG4gVE1MRV9iaWFzOiIsIGFicyhUcnVlX1BzaS10bWxlJGVzdGltYXRlc1tbMl1dW1sxXV0pKQpjYXQoIlxuIFJlbGF0aXZlX1RNTEVfYmlhczoiLGFicyhUcnVlX1BzaS10bWxlJGVzdGltYXRlc1tbMl1dW1sxXV0pL1RydWVfUHNpKjEwMCwiJSIpCmBgYAoKI1ItVE1MRSBpbXByb3ZpbmcgcHJlZGljdGlvbgoKSW4gYWRkaXRpb24gdG8gdGhlIGRlZmF1bHQgYWxnb3JpdGhtcyBpbXBsZW1lbnRlZCBpbiB0aGUgUi10bWxlIHBhY2thZ2UsIHdlIGNhbiBpbXByb3ZlIG91ciBlc3RpbWF0aW9uIGNhbGxpbmcgbW9yZSBlZmZpY2llbnQgbWFjaGluZSBsZWFyaW5nIGFsZ29yaXRobXMsIHN1Y2ggYXMgZ2VuZXJhbGl6ZWQgYWRkaXRpdmUgbW9kZWxzLCB0aGUgUmFuZG9tIEZvcmVzdCBhbmQsIFJlY3Vyc2l2ZSBQYXJ0aXRpb25pbmcgYW5kIFJlZ3Jlc3Npb24gVHJlZXM6ICAgIAoKYGBge3J9ClNMLlRNTEVSLlBzaSA8LSB0bWxlKFk9WSwgQT1BLCBXPXcsIGZhbWlseT0iYmlub21pYWwiLCAKICAgIFEuU0wubGlicmFyeSA9IGMoIlNMLmdsbSIsICJTTC5zdGVwIiwgIlNMLmdsbS5pbnRlcmFjdGlvbiIsICJTTC5nYW0iLCAiU0wucmFuZG9tRm9yZXN0IiwgIlNMLnJwYXJ0IiksCiAgICBnLlNMLmxpYnJhcnkgPSBjKCJTTC5nbG0iLCAiU0wuc3RlcCIsICJTTC5nbG0uaW50ZXJhY3Rpb24iLCAiU0wuZ2FtIiwgIlNMLnJhbmRvbUZvcmVzdCIsICJTTC5ycGFydCIpKQoKY2F0KCJTTC5UTUxFUi5Qc2k6IiwgU0wuVE1MRVIuUHNpJGVzdGltYXRlc1tbMl1dW1sxXV0sIjsiLCI5NSVDSSgiLCBTTC5UTUxFUi5Qc2kkZXN0aW1hdGVzW1syXV1bWzNdXSwiKSIpCmNhdCgiXG4gU0wuVE1MRVIuUHNpX2JpYXM6IiwgYWJzKFRydWVfUHNpLVNMLlRNTEVSLlBzaSRlc3RpbWF0ZXNbWzJdXVtbMV1dKSkKY2F0KCJcbiBSZWxhdGl2ZV9TTC5UTUxFUi5Qc2lfYmlhczoiLGFicyhUcnVlX1BzaS1TTC5UTUxFUi5Qc2kkZXN0aW1hdGVzW1syXV1bWzFdXSkvVHJ1ZV9Qc2kqMTAwLCIlIikKYGBgCgojQ29uY2x1c2lvbnMKV2UgaGF2ZSBkZW1vbnN0cmF0ZWQ6ICAKCjEuICoqVE1MRSBleGNlbHMqKiB0aGUgQUlQVFcgZXN0aW1hdG9yIGFuZCwgICAgCjIuIFRNTEUgKipiZXN0IHBlcmZvcm1hbmNlKiogaXMgb2J0YWluZWQgd2hlbiBjYWxsaW5nIG1vcmUgYWR2YW5jZWQgKipTdXBlci1MZWFybmVyKiogYWxnb3JpdGhtcy4gICAgIAogCiMgQXBwZW5kaXggT25lCkVmcm9uIGluIDE5ODIgc2hvd2VkIHRoYXQgdGhlIGVtcGlyaWNhbCAqKkluZmx1ZW5jZSBDdXJ2ZSoqIGVzdGltYXRlIG9mIHN0YW5kYXJkIGVycm9yIGlzIHRoZSBzYW1lIGFzIHRoZSBvbmUgb2J0YWluZWQgdXNpbmcgdGhlICoqaW5maW5pdGVzaW1hbCBqYWNra25pZmUqKiBhbmQgdGhlICoqbm9ucGFyYW1ldHJpYyBkZWx0YSBtZXRob2QqKiBbQGVmcm9uMTk4Ml0uICAKCjEuIFRoZSBEZWx0YSBNZXRob2QgdXNlcyB0aGUgZmlyc3Qgb3JkZXIgb2YgdGhlIFRheWxvciBzZXJpZXMgZXhwYW5zaW9uOiAKJCRmKHgpIFxhcHByb3ggZih4KVwsLVwsZihcbXUpXCwrXCwoeFwsLVwsXG11KWYnKFxtdSk7JCQgICAKd2hlcmUgJGYnKFxtdSkkIGlzIHRoZSBkZXJpdmF0aXZlIG9mIHRoZSBmdW5jdGlvbiB3aXRoIHJlc3BlY3QgdG8gWCBldmFsdWF0ZWQgYXQgdGhlIG1lYW4gb2YgWC4gVGhlcmVmb3JlLCBzcXVhcmluZyBib3RoIHRlcm1zLCB0aGUgdmFyaWFuY2UgaXMgYXBwcm94aW1hdGVseSBlc3RpbWF0ZSBhcyBmb2xsd29zOgokJFx0ZXh0e0V9W2YoeCktZihcbXUpXV57Mn1cYXBwcm94IFx0ZXh0e0V9KHhcLC1cLFxtdSleezJ9XCxcdGltZXNcLFtmJyhcbXUpXV57Mn07JCQKVGhlIGxlZnQtaGFuZCBzaWRlIG9mIHRoZSBhYm92ZSBlcXVhdGlvbiBpcyBhcHByb3hpbWF0ZWx5IHRoZSB2YXJpYW5jZSBvZiAkZih4KSQgYW5kIGFwcGxpZWQgdG8gdGhlIGVtcGlyaWNhbCBkaXN0cmlidXRpb24gb2YgWCwgdGhlIHNhbXBsZSBlc3RpbWF0ZSBvZiB2YXJpYW5jZSBmb3IgWCByZXBsYWNlczoKJCRcdGV4dHtFfSh4X3tpfVwsLVwsXG11KV57Mn0uJCQKVGhlIGluZmluaXRlc2ltYWwgamFja2tuaWZlIGVzdGltYXRlIG9mIHRoZSBzdGFuZGFyZCBlcnJvciBpcyBkZWZpbmVkIGFzIGZvbGxvd3M6CiQkU0Rfe2lqfShcdGhldGFfe2V9KVwsPVwsXGxlZnQoXGZyYWN7XHN1bV97aT0xfV57bn1VX3tpfV57Mn19e25eezJ9fVxyaWdodCleezEvMn07JCQKd2hlcmUgJFx0aGV0YV97ZX0kIGlzIHRoZSBlc3RpbWF0ZSBvZiB0aGUgcGFyYW1ldGVyICRcdGhldGEkIGFuZCAkVV97aX0kIGlzIGEgKipkaXJlY3Rpb25hbCBkZXJpdmF0aXZlKiogaW4gdGhlIGRpcmVjdGlvbiBvZiB0aGUgKmkqdGggY29vcmRpbmF0ZSBjZW50ZXJlZCBhdCB0aGUgbWVhbiBvZiB0aGUgZW1waXJpY2FsIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbi4KCiFbXShGaWd1cmVzL0lDLnBuZykKKipGaWd1cmUgMyoqLiBFc3RpbWF0ZSBvZiB0aGUgJFxwc2kkIFN0YW5kYXJkIEVycm9yIHVzaW5nIHRoZSBlZmZpY2llbnQgSW5mbHVlbmNlIEN1cnZlLiAgCioqSW1hZ2UgY3JlZGl0Kio6IE1pZ3VlbCBBbmdlbCBMdXF1ZS1GZXJuYW5kZXouIAoKI0FwcGVuZGl4IFR3bwpXaXRoIFRNTEUgd2UgY2FuIGNhbGwgdGhlIFItcGFja2FnZSAqKlN1cGVyLUxlYXJuZXIgKFNMKSoqLiBUaGUgKlNMKiB1c2VzICoqY3Jvc3MtdmFsaWRhdGlvbioqIGFuZCAqKmVuc2VtYmxlZCBsZWFybmluZyoqICh1c2luZyBhbGwgdGhlIHByZWRpY3Rpb25zIG9mIG11bHRpcGxlIHN0YWNrZWQgbGVhcm5pbmcgYWxnb3JpdGhtcykgdGVjaG5pcXVlcyB0byBpbXByb3ZlIG1vZGVsIHByZWRpY3Rpb24gcGVyZm9ybWFuY2UgW0BicmVpbWFuMTk5Nl0uICAKClRoZSAqKlNMKiogYWxnb3JpdGhtIHByb3ZpZGVzIGEgc3lzdGVtIGJhc2VkIG9uIFYtZm9sZCBjcm9zcy12YWxpZGF0aW9uIFtAZWZyb24xOTgzXSAoMTAtZm9sZHMpIHRvIGNvbWJpbmUgYWRhcHRpdmVseSBtdWx0aXBsZSBhbGdvcml0aG1zIGludG8gYW4gaW1wcm92ZWQgZXN0aW1hdG9yLCBhbmQgcmV0dXJucyBhIGZ1bmN0aW9uIHRoYW4gY2FuIGJlIHVzZWQgZm9yIHByZWRpY3Rpb24gaW4gbmV3IGRhdGFzZXRzLiAgCgohW10oRmlndXJlcy9jdi5wbmcpCioqRmlndXJlIDQqKjogMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uIGFsZ29yaXRobS4gIApUaGUgYmFzaWMgaW1wbGVtZW50YXRpb24gb2YgVE1MRSBpbiB0aGUgUi1wYWNrYWdlICoqdG1sZSoqIHVzZXMgYnkgZGVmYXVsdCB0aHJlZSBhbGdvcml0aG1zOiAgCjEuIFNMLmdsbSAobWFpbiB0ZXJtcyBsb2dpc3RpYyByZWdyZXNzaW9uIG9mIEEgYW5kIFcpLCAgICAKMi4gU0wuc3RlcCAoc3RlcHdpc2UgZm9yd2FyZCBhbmQgYmFja3dhcmQgbW9kZWwgc2VsZWN0aW9uIHVzaW5nIEFJQyBjcml0ZXJpb24sICAgcmVzdHJpY3RlZCB0byBzZWNvbmQgb3JkZXIgcG9seW5vbWlhbHMpIGFuZCwgIAozLiBTTC5nbG0uaW50ZXJhY3Rpb24gKGEgZ2xtIHZhcmlhbnQgdGhhdCBpbmNsdWRlcyBzZWNvbmQgb3JkZXIgcG9seW5vbWlhbHMgYW5kIHR3byBieSB0d28gaW50ZXJhY3Rpb25zIG9mIHRoZSBtYWluIHRlcm1zIGluY2x1ZGVkIGluIHRoZSBtb2RlbCkuICAgIAoKVGhlIHByaW5jaXBhbCBpbnRlcmVzdCBvZiBjYWxsaW5nIHRoZSBTdXBlci1MZWFybmVyIGlzIHRvIG9idGFpbiB0aGUgbGVzcy11bmJpYXNlZCBlc3RpbWF0ZWQgZm9yICRcYmFyIFFfe259XnswfShBLFcpJCBhbmQgJGdfezB9KEEsVykkLiBJdCBpcyBhY2hpZXZlZCBieSBvYnRhaW5pbmcgdGhlIHNtYWxsZXN0IGV4cGVjdGVkIGxvc3MgZnVuY3Rpb24gZm9yIFkgb3IgQSAoYmluYXJ5IG91dGNvbWVzKSwgcmVzcGVjdGl2ZWx5LiBGb3IgaW5zdGFuY2UsIHRoZSBuZWdhdGl2ZSBsb2dhcml0aG1pYyBsb3NzIGZ1bmN0aW9uIGZvciBZIGlzIGNvbXB1dGVkIGFzIHRoZSBtaW5pbWl6ZXIgb2YgdGhlIGV4cGVjdGVkIHNxdWFyZWQgZXJyb3IgbG9zczogICAKJCRcYmFyIFFfezB9XCw9XCwgXHRleHR7YXJnIG1pbn1fe1xiYXIgUX1FX3swfUwoTywgXGJhciBRKSwkJCAgIAp3aGVyZSAkTChPLCBcYmFyIFEpJCBpczoKJCQgKFkgXCwtXCwgXGJhciBRKEEsIFcpKV57Mn0kJApUaGUgKipTTCoqIGFsZ29yaXRobSBmaXJzdCBzcGxpdCB0aGUgZGF0YSBpbnRvIHRlbiBibG9ja3MgYW5kIGZpdHMgZWFjaCBvZiB0aGUgc2VsZWN0ZWQgYWxnb3JpdGhzIG9uIHRoZSB0cmFpbmluZyBzZXQgKG5vbi1zaGFkZWQgYmxvY2tzKSwgdGhlbiBwcmVkaWN0cyB0aGUgZXN0aW1hdGVkIHByb2JhYmlsaXRpZXMgb2YgdGhlIG91dGNvbWUgKFkpIHVzaW5nIHRoZSB2YWxpZGF0aW9uIHNldCAoc2hhZGVkIGJsb2NrKSBmb3IgZWFjaCBhbGdvcml0aG0sIGJhc2VkIG9uIHRoZSBjb3JyZXNwb25kaW5nIHRyYWluaW5nIHNldC4gQWZ0ZXJ3YXJkcywgdGhlICoqU0wqKiBlc3RpbWF0ZXMgdGhlIHRoZSBjcm9zcy12YWxpZGF0aW5nIHJpc2sgZm9yIGVhY2ggYWxnb3JpdGhtIGF2ZXJhZ2luZyB0aGUgcmlza3MgYWNyb3NzIHZhbGlkYXRpb24gc2V0cyByZXN1bHRpbmcgaW4gb25lIGVzdGltYXRlZCBjcm9zcy12YWxpZGF0ZWQgcmlzayBmb3IgZWFjaCBhbGdvcml0aG0uIEZpbmFsbHksIHRoZSAqKlNMKiogc2VsZWN0cyB0aGUgY29tYmluYXRpb24gb2YgWiB0aGF0IG1pbmltaXNlcyB0aGUgY3Jvc3MtdmFsaWRhdGlvbiByaXNrLCBkZWZpbmVkIGFzIHRoZSBtaW5pbXVtIG1lYW4gc3F1YXJlIGVycm9yIGZvciBlYWNoIG9mIHRoZSBzZWxlY3RlZCBhbGdvcml0aG1zIHVzaW5nIFkgYW5kIFouIEEgd2VpZ2h0ZWQgY29tYmluYXRpb24gb2YgdGhlIGFsZ29yaXRobXMgKGVuc2VtYmxlIGxlYXJuaW5nKSBpbiBaIGlzIHRoZW4gdXNlZCB0byBwcmVkaWN0IHRoZSBvdXRjb21lIChZKSAoc2VlIEZpZ3VyZSA1KS4gIAoKIVtdKEZpZ3VyZXMvU0wucG5nKQoKKipGaWd1cmUgNSoqOiBGbG93IERpYWdyYW0gZm9yIHRoZSBTdXBlci1MZWFybmVyIGFsZ29yaXRobS4gIAoqKkFkYXB0ZWQgZnJvbSoqOiBNYXJrIHZhbiBkZXIgTGFhbiBhbmQgU2hlcnJpIFJvc2UuIFRhcmdldGVkIGxlYXJuaW5nOiBjYXVzYWwgaW5mZXJlbmNlIGZvciBvYnNlcnZhdGlvbmFsIGFuZCBleHBlcmltZW50YWwgZGF0YVNwcmluZ2VyIFNlcmllcyBpbiBTdGF0aXN0aWNzLCAyMDExLiAKCiNBYmJyZXZpYXRpb25zCgpUTUxFOiBUYXJnZXRlZCBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiAgClNMOiBTdXBlciBMZWFybmVyICAKSVBUVzogSW52ZXJzZSBwcm9iYWJpbGl0eSBvZiB0cmVhdG1lbnQgd2VpZ2h0aW5nICAKQUlQVFc6IEF1Z21lbnRlZCBpbnZlcnNlIHByb2JhYmlsaXR5IG9mIHRyZWF0bWVudCB3ZWlnaHRpbmcgIApNU0U6IE1lYW4gc3F1YXJlZCBlcnJvciAgClNFOiBTdGFuZGFyZCBlcnJvciAgCkVFOiBFc3RpbWF0aW9uIGVxdWF0aW9ucyAgIApHTU06IEdlbmVyYWxpc2VkIG1ldGhvZCBvZiBtb21lbnRzICAKTzogT2JzZXJ2ZWQgb3JkZXJlZCBkYXRhIHN0cnVjdHVyZSAgClc6IFZlY3RvciBvZiBjb3ZhcmlhdGVzICAKQTogQmluYXJ5IHRyZWF0bWVudCBvciBleHBvc3VyZSAgClk6IEJpbmFyeSBvdXRjb21lICAKJFlfezF9LCBZX3tPfSQ6IENvdW50ZXJmYWN0dWFsIG91dGNvbWVzIHdpdGggYmluYXJ5IHRyZWF0bWVudCBBICAKJFBfezB9JDogVHJ1ZSBkYXRhLWdlbmVyYXRpbmcgZGlzdHJpYnV0aW9uICAKJFxQc2koUF97MH0pJDpUcnVlIHRhcmdldCBwYXJhbWV0ZXIgIAokXHBzaV97MH1cLD1cLFxQc2koUF97MH0pJDogVHJ1ZSB0YXJnZXQgcGFyYW1ldGVyIHZhbHVlICAKJGdfezB9JDogUHJvcGVuc2l0eSBzY29yZSBmb3IgdGhlIHRyZWF0bWVudCBtZWNoYW5pc20gKEEpICAgICAgCiRnX3swfSQ6IEVzdGltYXRlIG9mICRnX3swfSQgIAokXGVwc2lsb24kOiBGbHVjdHVhdGlvbiBwYXJhbWV0ZXIgIAokXGVwc2lsb25fe259JDogRXN0aW1hdGUgb2YgJFxlcHNpbG9uJAokSF97bn1eeyp9JDogQ2xldmVyIGNvdmFyaWF0ZSBlc3RpbWF0ZSAoaW52ZXJzZSBwcm9iYWJpbGl0eSBvZiB0cmVhdG1lbnQgd2VpZ2h0KSAgICAgIAokTChPLFxiYXIgUSkkOiBFeGFtcGxlIG9mIGEgbG9zcyBmdW5jdGlvbiB3aGVyZSBpdCBpcyBhIGZ1bmN0aW9uIG9mIE8gYW5kICRcYmFyIFEkICAgCiQoWSBcLC1cLCBcYmFyIFEoQSwgVykpXnsyfSQ6IEV4cGVjdGVkIHNxdWFyZWQgZXJyb3IgbG9zcyAgIAokXGJhciBRX3swfSQ6IENvbmRpdGlvbmFsIG1lYW4gb2Ygb3V0Y29tZSBnaXZlbiBwYXJlbnRzOyAkRV97MH0oWXxBLFcpJCAgCiRcYmFyIFFfe259JDogRXN0aW1hdGUgb2YgJFxiYXIgUV97MH0kICAKJFxiYXIgUV97bn1eezB9JDogSW5pdGlhbCBlc3RpbWF0ZSBvZiAkXGJhciBRX3swfSQgICAgCiRcYmFyIFFfe259XnsxfSQ6IEZpcnN0IHVwZGF0ZWQgZXN0aW1hdGUgb2YgJFxiYXIgUV97MH0kICAgCiRcYmFyIFFfe259XnsqfSQ6IFRhcmdldGVkIGVzdGltYXRlIG9mICRcYmFyIFFfe259XnswfSQgaW4gVE1MRSBwcm9jZWR1cmU7ICRcYmFyIFFfe259XnsqfSQgbWF5IGVxdWFsICRcYmFyIFFfe259XnsxfSQgIAoKI1Nlc3Npb24gSW5mbyAKYGBge3Igc2Vzc2lvbi1pbmZvLCByZXN1bHRzID0nbWFya3VwJ30KZGV2dG9vbHM6OnNlc3Npb25faW5mbygpCmBgYAoKI1RoYW5rIHlvdSAgClRoYW5rIHlvdSBmb3IgcGFydGljaXBhdGluZyBpbiB0aGlzIHR1dG9yaWFsLiAgCklmIHlvdSBoYXZlIHVwZGF0ZXMgb3IgY2hhbmdlcyB0aGF0IHlvdSB3b3VsZCBsaWtlIHRvIG1ha2UsIHBsZWFzZSBzZW5kIDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9taWdhcmlhbmUvTUFMRiIgdGFyZ2V0PSJfYmxhbmsiPm1lPC9hPiBhIHB1bGwgcmVxdWVzdC4KQWx0ZXJuYXRpdmVseSwgaWYgeW91IGhhdmUgYW55IHF1ZXN0aW9ucywgcGxlYXNlIGUtbWFpbCBtZS4gCllvdSBjYW4gY2l0ZSB0aGlzIHJlcG9zaXRvcnkgYXM6ICAgICAgICAKTHVxdWUtRmVybmFuZGV6IE1BLCAoMjAxNikuIFRhcmVnZXRlZCBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGlvbiBmb3IgYSBCaW5hcnkgT3V0Y29tZTogVHV0b3JpYWwgYW5kIEd1aWRlZCBJbXBsZW1lbnRhdGlvbi4gR2l0SHViIHJlcG9zaXRvcnksIGh0dHA6Ly9taWdhcmlhbmUuZ2l0aHViLmlvL1RNTEUubmIuaHRtbC4gICAgCioqTWlndWVsIEFuZ2VsIEx1cXVlIEZlcm5hbmRleioqICAgICAKKipFLW1haWw6KiogKm1pZ3VlbC1hbmdlbC5sdXF1ZSBhdCBsc2h0bS5hYy51ayogIAoqKlR3aXR0ZXIqKiBgQFdBVFpJTEVJYCAgCgojIFJlZmVyZW5jZXMgCg==